Jul 3, 2014

C Programming #35: Preprocessor

Following article will discuss about C pre-processor. Already we have discussed what is C pre-processing, where it stands in entire process of compilation. You can read it hereAll C pre-processor statement start with # in them. Lets us go though all them in detail 

#define

#define is very important C pre-processor which is already discussed here.


Conditional - simple #if

It provides power of decision making in pre-processing

Syntax:

#if CONST_EXPRESSION
/* Some Code here */
#else

Note that after #if it should be constant expression. It cannot contain variables. Very conman use of it is to enable few code only for debugging.

Lets take factorial as example with debug-


#include <stdio.h>
int factorial(int);
int main()
{
   int a = 10;
   int fact;

   fact = factorial (4);

   printf("Factorial of %d is %d\n", fact);
 
   return 0;
}

#define DEBUG 1 /* 1 - Enable the debug 
                   0 - Disable the debug */

int factorial(int n);
{
   int counter, ret_fact;
#if DEBUG
   printf("Factorial is invoked for n = %d\n", n);
#endif
   for(counter = 1, ret_fact =1; counter <= n; counter++) {
        ret_fact *= counter;
#if DEBUG
 printf("Counter - %d; temp_Fact - %d\n", counter, ret_fact);
#endif
   }
#if DEBUG
        printf("Returning from Factorial with ret value %d\n", ret_fact);
#endif
    return ret_fact;
}

There are lot of printf in DEBUG, these printf are introduced in such a manner so as to get insight into code execution. We do not always want these debug as it would produce too much output, some time specifically we want it enabled, when we want to debug. Whole thing would be more elegantly done, but nevertheless it gives the idea.

Hence by doing 

#define DEBUG 1  -> we enable the debugs

#define DEBUG 0  -> we disable the debug


Factorial is invoked for n = 4
Counter - 1; temp_Fact - 1
Counter - 2; temp_Fact - 2
Counter - 3; temp_Fact - 6
Counter - 4; temp_Fact - 24
Returning from Factorial with ret value 24
Factorial of 4 is 24

output with debug disabled - 

Factorial of 4 is 24


Conditional - #if, #else


Just like if else covered here, we have #if #else also in preprocessor.

Syntax is as follows

#if COND_EXPRESSION
// Do this
#else
// Do that
#endif

Say we have to write function prototype depending on Linux kernal version 

#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)
int x_func(int, int);
#else
int x_func(int, int, float);
#endif

LINUX_VERSION_CODE is a macro that will have kernel version as some number in it. KERNEL_VERSION is another macro that will convert version 2.6.16 into similar number. Hence x_func will have 2 parameter when compiled for kernel version <= 2.6.16 and will have 3 parameter when kernel version > 2.6.16.


Conditional - #if elif #else ladder.


Syntax:

#if COND_EXPR_1
// Something 1
#elif COND_EXPR_2
// Something 2
#elif COND_EXPR_3
// Something 3
#else
// Something 4
#endif

Typical example would be when writing generic code which would handle all OS.

#if LINUX
// Call linux API
#elif WIN32 
// Call windows API
#else
#error "Platform not supported"
#endif

If macro LINUX is true then Linux API is called else WIN32 it will cal windows API. If none is true then #error is hit, and compilation stops giving error message Platform not supported.


#ifdef vs #if defined



There are two deviation of #if 

  1. #ifdef 
  2. #if defined


#if checks the CONST_EXPR to be true/false. Sometimes check would be to check if MACRO is just defined (Does not matter what value it is defined), then in such case #ifdef is used.

Previous example could be written as follows

#ifdef LINUX
// Call linux API
#elif defined(WIN32)
// Call windows API
#else
#error "Platform not supported"
#endif

Note that 

  1. #ifdef LINUX and #if defined(LINUX) both means the same.
  2. There is nothing like #elifdef hence in such case we need to use defined.
  3. defined allows check of more than one macro in one preprocessor statement e.g

#if defined(LINUX) && defined(ANDROID)
// Statement 1
#endif

Statement is enabled only if both LINUX and ANDROID are defined. Above example cannot be written using #ifdef. Hence we now know the similarities and differences of #if defined and #ifdef.


#error

This is already covered, in one of the example above.

Syntax:

#error "Error message "

When this statement is hit during pre-processing, it stops pre-processing and gives error to user.


#warning

Syntax:

#warning "Warning message"

It is very similar to error but it wont stop the pre-processing. But warning message would be still printed out.


#undef

Sometimes we require particular #define to be undefined this can be done by #undef.

Example

#define X 10

//

// Section 1
//


#undef 


//

// Section 2
//

#define X 100


//

// Section 3
// 

In Section 1 value of X will be 10, in Section 2 X will be not defined at all (all reference to X will result in compilation error) in Section 3 its value is 100.


Special #defines

There are some special macro supported by most compiler which will be very useful.

__FILE__ -> Returns the file name
__func__ -> Returns the function name
__LINE__ -> Returns current line number

Typical usage is as follows


#include <stdio.h>

void function_x(void);

int main()
{
   printf("File name - %s\n", __FILE__);
   printf("Func name - %s\n", __func__);
   printf("Line number - %d\n", __LINE__);
 
   function_x();

   return 0;

}
void function_x(void)
{
   printf("File name - %s\n", __FILE__);
   printf("Func name - %s\n", __func__);
   printf("Line number - %d\n", __LINE__);

   return;
}

Output of above program is


File name - prog.c
Func name - main
Line number - 9
File name - prog.c
Func name - function_x
Line number - 20

Hopefully this has given you good overview of all C pre-processor.

Links

Next Article - C Programming #36: Parameterised Macro
Previous Article - C Programming #34: Journey from source code to executable
All Article - C Programming

No comments :

Post a Comment