Before the compiler starts generating the executable it parses through the source text. Most compilers supply a set of commands, which allows some manipulation of the parsing process. They are normally indicated with the pound symbol (#) as the first character and ends with each line breake in the source text (this is in strong contrast to the C language itself because there white spaces such as line breaks are ignored). Note that these compiler instructions does not belong to the language specification of C and they are completely evaluated durinng compilation time.
The #include statement allows to import other source text into the current source text. To avoid confusion it is standard that only include-files are the target of the include instruction. Two formats are allowed:
Note that the compiler instruction does not require a semi-colon to indicate the end of the instruction. this is done by the line break. The difference is that with the quotation marks the compiler looks first in the current directory before searching other directories (e.g. the include directory for the C library such as
/usr/include on Linux/Unix/Mac). More directories can be added to the search path with the -Ipath option for the compilatgion command (e.g. gcc). In general the system include files are imported with the first syntax, while your own include files are using the second syntax.
The #define instruction allows to create alias or macros, which then can be used in the source text. Note that macros can also be defined as the -D option for the compiler command gcc.
The simplest form is to create an empty definition/macro, e.g.
This is very useful for conditional compilations (see below). Note that it is a good programming style to use all capital letters for macro names, so that someone can identify in the source text.
The second form defines a macro and assigns a string, e.g.
#define PI 3.14159
Once defined the macro name can be used as a replacement for the actual string, e.g.
a=sin(PI*0.5); Note that there are alternatives to this methode, e.g. by defining the global constant
const double pi = 3.1415;. Note the difference the later method allocates memory which can be access during run-time, while the use of a macro replaces the content during compilation time (e.g. the sine-function above is converted to the source text
a=sin(3.14159*0.5); before it is converted into an executable instruction by the compiler.
The last method is the definition of a function macro, e.g.
#define MAX(x,y) x > y ? x : y
However it is strongly recommended not to use it in this way because it is a source of very subtle errors, e.g. using it in this context the command
is converted to
a=1e5*x1 > x2 ? x1 : x2;
Because multiplication has a lower precendence level than the comparison, x1 is multiplicated with 1e5 before the comparison is executed. As a result a gets only the value of x2 assigned, when x2 is 10000 times larger than x1, which is not the intention of the MAX function.
The complimentary instruction to delete any macro definition. The macro name is mandatory, e.g.
With this instruction definition can be only available for a subsection of the complete source text, or to allow the redifinition of macros:
#define PI 3.14
The compiler instructions
#ifdef macro name and the closing
#endif allows for conditional compilation. The source text between the two tags are only included if the macro is defined. The prime example is to allow for more output in a debugging stage:
printf("debugging: a = %d\n", a);
If the macro DEBUG is defined during the content of variable a is printed to screen. This helps to trace possible errors without using the debugger program. Once (hopefully) all errors are eliminated the definition of DEBUG can be removed and the program can be compiler as the final version for release. Using this method the programmer does not need to remove all the print statements before creating the release version.
A similar instruction is
#ifndef, which works similar to the
#ifdef instruction, except that the following macro must NOT exist in order to include the following source text.
Most compilers supply a set of predefined macros, which can slightly vary, depending on the compiler. The following table lists the most common ones.
|NULL||(void *) 0|
|__DATE__||String with the current date|
|__FILE__||String with the file name (of the executable)|
|__TIME__||String with the current time|
|isalpha(c)||Checks whether c is a letter|
|isdigit(c)||Checks whether c is a digit|
|isprint(c)||Checks whether c is a printable character|
|iscntrl(c)||Checks whether c is a control character (e.g. carriage return)|
|islower(c)||Checks whether c is a lower case character|
|isupper(c)||Checks whether c is an upper case character|
|tolower(c)||Converts c to a lower case character|
|toupper(c)||Converts c to an upper case character|
|assert(a)||Prints error message and terminates program when the boolean value a is true|