Quick Navigation Bar basic c :: functions, preprocessor :: input/output and file i/o [ toc | forums ] |
Note: If the document URL does not begin with https://randu.org/tutorials/c/ then you are viewing a copy. Please direct your browser to the correct location for the most recent version. |
Now that we have a understanding of the very basics of C, it is time now to turn our focus over to making our programs not only run correctly but more efficiently and are more understandable.
char *strdup(char *s) int add_two_ints(int x, int y) void useless(void)The first function header takes in a pointer to a string and outputs a char pointer. The second header takes in two integers and returns an int. The last header doesn't return anything nor take in parameters.
int add_two_ints(int x, int y)
return
. The return value must be the
same type as the return type specified in the function's interface.getopt
.
But since this function is not part of ANSI C, you must declare the
function prototype, or you will get implicit declaration warnings when
compiling with our flags. So you can simply prototype getopt(3) from the
man pages:
/* This section of our program is for Function Prototypes */ int getopt(int argc, char * const argv[], const char *optstring); extern char *optarg; extern int optind, opterr, optopt;So if we declared this function prototype in our program, we would be telling the compiler explicitly what getopt returns and it's parameter list. What are those extern variables? Recall that
extern
creates a reference to variables across
files, or in other words, it creates file global scope for those
variables in that particular C source file. That way we can access
these variables that getopt modifies directly. More
on getopt on the next section about Input/Output.int applyeqn(int F(int), int max, int min) { int itmp; itmp = F(int) + min; itmp = itmp - max; return itmp; }What does this function do if we call it with
applyeqn(square(x), y, z);
? What happens is that the
int F(int)
is a reference to the function that is passed
in as a parameter. Thus inside applyeqn
where there is
a call to F
, it actually is a call to square
!
This is very useful if we have one set function, but wish to vary the
input according to a particular function. So if we had a different
function called cube
we could change how we call
applyeqn
by calling the function by
applyeqn(cube(x), y, z);
.void swap(int x, int y) { int tmp = 0; tmp = x; x = y; y = tmp; }If you were to simply pass in parameters to this swapping function that swaps two integers, this would fail horribly. You'll just get the same values back.
#
. This listing
is from Weiss pg. 104. The unconditional directives are:
#include
- Inserts a particular header from another
file#define
- Defines a preprocessor macro#undef
- Undefines a preprocessor macro#ifdef
- If this macro is defined#ifndef
- If this macro is not defined#if
- Test if a compile time condition is true#else
- The alternative for #if#elif
- #else an #if in one statement#endif
- End preprocessor conditional#
- Stringization, replaces a macro parameter with
a string constant##
- Token merge, creates a single token from two
adjacent ones#define MAX_ARRAY_LENGTH 20Tells the CPP to replace instances of MAX_ARRAY_LENGTH with 20. Use
#define
for constants to increase readability. Notice
the absence of the ;
.
#include <stdio.h> #include "mystring.h"Tells the CPP to get stdio.h from System Libraries and add the text to this file. The next line tells CPP to get mystring.h from the local directory and add the text to the file. This is a difference you must take note of.
#undef MEANING_OF_LIFE #define MEANING_OF_LIFE 42Tells the CPP to undefine MEANING_OF_LIFE and define it for 42.
#ifndef IROCK #define IROCK "You wish!" #endifTells the CPP to define IROCK only if IROCK isn't defined already.
#ifdef DEBUG /* Your debugging statements here */ #endifTells the CPP to do the following statements if DEBUG is defined. This is useful if you pass the
-DDEBUG
flag to gcc. This will
define DEBUG, so you can turn debugging on and off on the fly!int square(int x) { return x * x; }We can instead rewrite this using a macro:
#define square(x) ((x) * (x))A few things you should notice. First
square(x)
The
left parentheses must "cuddle" with the macro identifier. The next
thing that should catch your eye are the parenthesis surrounding the
x's. These are necessary... what if we used this macro as square(1 + 1)?
Imagine if the macro didn't have those parentheses? It would become
( 1 + 1 * 1 + 1 ). Instead of our desired result of 4, we would get
3. Thus the added parentheses will make the expression
( (1 + 1) * (1 + 1) ). This is a fundamental difference between
macros and functions. You don't have to worry about this with functions,
but you must consider this when using macros.#define swap(x, y) { int tmp = x; x = y; y = tmp }Now we have swapping code that works. Why does this work? It's because the CPP just simply replaces text. Wherever swap is called, the CPP will replace the macro call with the defined text. We'll go into how we can do this with pointers later.
Notice: Please do not replicate or copy these pages and
host them elsewhere. This is to ensure that the latest version can always
be found here.
Disclaimer: The document author has published these pages
with the hope that it may be useful to others. However, the document
author does not guarantee that all information contained on these
webpages are correct or accurate. There is no warranty, expressed or
implied, of merchantability or fitness for any purpose. The author does
not assume any liability or responsibility for the use of the information
contained on these webpages.
If you see an error, please send an email to the address below indicating
the error. Your feedback is greatly appreciated and will help to
continually improve these pages.