The C preprocessor

The C preprocessor is the first step of the code translation process. It performs some editing tasks before the compiler starts . With Standard C has come a number of useful and powerful techniques that make the preprocessor a genuinely useful tool to the C programmer. The preprocessor is a powerful but must be used carefully (pitfalls)

normally you don’t see the preprocessor output , you can see it by using the -E flag:

# gcc -E samp.c

One duty of the preprocessor is to remove comments, and replace it with blank lines. If any header files are included, these will also appear in the preprocessor output. This can result in a lot of text.

Some preprocessor identifiers:

__LINE__ - Number of line being compiled

__FILE__ - Name of file being compiled

__DATE__ - Compilation date "mm dd yyyy"

__TIME__ - Compilation time "hh:mm:ss"

__func__ - Name of the current function

For Example you can use it for debug:

 printf("Error in line %d in file %s\n", __LINE__, __FILE__);

Macros and pitfalls:

If we define the following macro:

#define MAX(a,b) (a>b)?a:b

If we use it with simple parameters it will work fine but if we put some complexity it can cause some pitfalls for example:

k = MAX(i++, j++);

Will expand to:

k = ((i++) > (j++)) ? (i++) : (j++));

The result is that one of the arguments will increment twice

Names conflicts

We can define a function with the same name as a predefined macro but we need to use parentheses :

#include<stdio.h>

#define max(a,b) (a>b)?a:b+9; 

int (max)(int a, int b)
{ 
return (a > b) ? a : b+99;
}

int main(void)
{
    int i = 10, j = 100, k1,k2;
    k1 = (max)(i, j); // function call
    k2 = max(i, j);  // macro call

   printf("%d\n%d\n",k1,k2);

}

The preprocessor will only substitute a macro when the macro is followed by an opening parenthesis (intervening whitespace is allowed). Placing parentheses around the macro name effectively removes the name from the opening parenthesis meaning the preprocessor will leave it alone. It is valid to place parentheses around any C statement without effect

The # operator

The # operator converts macro parameters into strings.

PRINTVAL(ex) printf("%s\n", #ex)

PRINTVAL(f + 10);  // printf("%s\n", "f + 10");

The ## operator

The ## operator provides a portable way of merging separate tokens into one single token.It takes the macro parameters to its right and left and pastes them together. It cannot be the first or last item in a macro.

#define DISPLAY(x) printf("file%sname = %s\n", #x, file##x##name)
const char * file1name  =  "autoexec.bat";
const char * file2name  =  "config.sys";
const char * file3name  =  "command.com";
const char * file4name  =  "ibmbio.com";

DISPLAY(1); //OK
DISPLAY(2); //OK
DISPLAY(3); //OK
DISPLAY(4); //OK

for(i=0;i<4;i++)
   DISPLAY(i); // Wrong

String Concatenation

Standard C preprocessors concatenate adjacent strings in the input program

printf("hello" "world");

#if

#if can be used to test the value of preprocessor constants. A constant integer expression is evaluated. This may not include sizeof, any casts or any enumerated types.

#if SYMBOL == 4
#if SYMBOL > 4
#if SYMBOL1 == SYMBOL2

The block should ends with #endif and you can use #elif for complex tests

#ifdef and #if defined

#ifdef can be used to determine if a preprocessor symbol has been defined.

#ifdef SYMBOL
... .
#endif /* SYMBOL

This may be replaced by the defined operator that is more flexible than #ifdef:

#if defined(SYM_1) && defined(SYM_2)

....

#endif

Passing Types Into a Macro

Since the preprocessor does not understand C, the following is possible:

#define  SWAP(type, var1, var2) \
{ 		     \
	type   _temp_  =  var1; 	  \
	var1 = var2; 	     \
	var2 = _temp_; 	     \
}

SWAP(int, i, j)
SWAP(long double, ld1, ld2)
SWAP(date, s1, s2)

 

Tagged