Learn C - C Preprocessing






Preprocessing of source code occurs before it's compiled to machine instructions.

The preprocessing phase can execute a range of operations specified by preprocessing directives, which are started by the # symbol.

The preprocessing phase manipulates C source code prior to compilation.

After the preprocessing phase, all directives will be analyzed and executed and all preprocessing directives will no longer appear in the source code.

#include and #define are two popular directives.

Define constants with #define statement.


#include <stdio.h> 
#define PI 3.14159 //from  ww w  .j  a  v  a2s  .co  m
int main(void) 
{ 
    float area, circum, radius; 
 
    printf("What is the radius of your pizza?\n"); 
    scanf("%f", &radius); 
    area = PI * radius * radius; 
    circum = 2.0 * PI *radius; 
    printf("Your basic pizza parameters are as follows:\n"); 
    printf("circumference = %1.2f, area = %1.2f\n", circum, 
           area); 
    return 0; 
}    

The code above generates the following result.





Including Header Files

You're completely familiar with statements such as this:

#include <stdio.h>

The code above brings the contents of the standard library header file into your program.

A file introduced into your program by an #include directive may also contain #include directives.

Defining Your Own Header Files

You can define your own header files, usually with the extension .h.

Header files should not include implementation.

You create header files to contain declarations.

All your function definitions and global variables are placed in source files with the extension .c.

You can place function prototypes, struct type definitions, symbol definitions, extern statements, and typedefs in a header file.

A typical example might be this:

#include "myfile.h"

External Variables

To use a global variable from another file, declare the variable as external to the current source file using the extern keyword.

For example, suppose you have global variables defined in another file by the statements:

int number = 0; 
double in_to_mm = 2.54; 

In a source file in which you want to access these, specify that these variable names are external:

extern int number; 
extern double in_to_mm; 

These statements don't create these variables.

They inform the compiler that these names are defined else where.





Static Functions

You can ensure that a function is only visible within the source file in which you define it by declaring it as static.

For example:

static double average(double x, double y) { return (x + y) / 2.0; }

Substitutions in Your Program Source Code

The general form of define preprocessor directive is the following:

#define identifier sequence_of_characters 

Here, identifier conforms to the usual definition of an identifier in C.

Define String in #define statement.


#include <stdio.h> 
#define PRAISE "You are an extraordinary being." 
int main(void) 
{ /* www.  ja va 2 s  .  c  o m*/
    char name[40]; 
 
    printf("What's your name? "); 
    scanf("%s", name); 
    printf("Hello, %s. %s\n", name, PRAISE); 
 
    return 0; 
}    

The code above generates the following result.

Macros

A macro is preprocessor allowing what might be called multiple parameterized substitutions.

This involves substitution of a sequence of characters for a token identifier.

An example will make this easier to understand:

#define Print(My_var) printf_s("%d", My_var) 

My_var is a parameter name, for which you can specify a string.

This directive provides for two levels of substitution.

An occurrence of Print(My_var) in your code will be replaced by the string immediately following it and with whatever argument you specify for My_var.

You could, for example, write the following:

Print(ival);

This will be converted during preprocessing to this statement:

printf_s("%d", ival);

Macros Functions

The general form of the kind of substitution directive just discussed is the following:

#define macro_name( list_of_identifiers ) substitution_string

The following code shows how to define a macro for producing a maximum of two values with the following directive:

#define max(x, y) x>y ? x : y

You can then put the statement in the program:

int result = max(myval, 99);

This will be expanded during preprocessing to produce the following code:

int result = myval>99 ? myval : 99; 

Conditional Compilation

We can test whether an identifier exists as a result of having been created in a previous #define directive.

It takes the following form:

#if defined identifier 
// Statements... 
#endif 

If the specified identifier is defined by a #define directive prior to this point, statements that follow the #if are included in the program code until the directive #endif.

If the identifier isn't defined, the statements between the #if and the #endif will be skipped.

You can test for the absence of an identifier.

The general form of this directive is:

#if !defined identifier 
// Statements... 
#endif 

Here the statements following the #if down to the #endif will be included if identifier hasn't previously been defined.

This can avoid duplicating functions, or other blocks of code and directives.

The mechanism is simply to top and tail the block of code you want to avoid duplicating as follows:

#if !defined block1 
  #define block1 
  /* Statements you do not          */ 
  /* want to occur more than once.  */ 
#endif 

Testing for Multiple Conditions

You can use logical operators to test if multiple identifiers have been defined. For example:

#if defined block1 && defined block2 
   // Statements... 
#endif 

This will evaluate to true if both block1 and block2 have previously been defined.

You can use the || and ! operators in combination with && if you really want to go to town.

Undefining Identifiers

We can undefine an identifier you've previously defined.

This is achieved using a directive such as:

#undef block1

If block1 was previously defined, it is no longer defined after this directive.

Testing for Specific Values for Identifiers

You can use a form of the #if directive to test the value of a constant expression.

If the value of the constant expression is nonzero, the following statements down to the next #endif are included in the program code.

If the constant expression evaluates to zero, the following statements down to the next #endif are skipped.

The general form of the #if directive is:

#if constant_expression

You might have the following sequence of statements, for example:

#if CPU == Intel
  printf("Performance should be good.\n" ); 
#endif 

Multiple-Choice Selections

You have the #else directive.

It identifies a group of directives to be executed or statements to be included if the #if condition fails, for example:

#if CPU == Intel
  printf_s("Performance should be good.\n" ); 
#else 
  printf_s("Performance may not be so good.\n" ); 
#endif 

#elif directive has the general form:

#elif constant_expression

Standard Preprocessing Macros

The __DATE__ macro generates a string representation of the date in the form Mmm dd yyyy.

Here Mmm is the month in characters, such as Jan, Feb, and so on.

The pair of characters dd is the day in the form of a pair of digits 1 to 31, where single-digit days are preceded by a space.

Finally, yyyy is the year as four digits-2012, for example.

__TIME__ provides a string containing the value of the time in the form hh:mm:ss.

The time is when the compiler is executed, not when the program is run.

You could use this macro to record in the output when your program was last compiled with this statement:

printf_s("Program last compiled at %s on %s\n", __TIME__, __DATE__ );

Executing this statement will produce output similar to the following:

Program last compiled at 15:27:01 on Nov 24 2015 

The __FILE__ macro represents the name of the current source file as a string literal.

The __LINE__ macro contains an integer constant corresponding to the current line number.

You can use this in combination with the __FILE__ macro to identify where in the source code a particular event or error has been detected.

For example:

fprintf(stderr, "Failed to open file in %s line %d\n", __FILE__, __LINE__);