文章目录
Preprocessor Directives(指令)
The C preprocessor uses the # directives to make substitutions in program source code before compilation.
For example, the line #include <stdio.h> is replaced by the contents of the stdio.h header file before a program is compiled.
Preprocessor directives and their uses:
#include Including header files.
#define, #undef Defining and undefining macros.
#ifdef, #ifndef, #if, #else, #elif, #endif Conditional compilation.
#pragma Implementation and compiler specific.
#error, #warning Output an error or warning message An error halts compilation.
Do NOT put a semicolon character at the end of a # directive.
The #include Directive
The #include directive is for including header files in a program. A header file declares a collection of functions and macros for a library, a term that comes from the way the collection of code can be reused.
Some useful C libraries are:
stdio input/output functions, including printf and file operations.
stdlib memory management and other utilities
string functions for handling strings
errno errno global variable and error code macros
math common mathematical functions
time time/date utilities
Corresponding header files for the libraries end with .h by convention(按照惯例). The #include directive expects brackets <> around the header filename if the file should be searched for in the compiler include paths.
A user-defined header file is also given the .h extension, but is referred to with quotation marks, as in “myutil.h”. When quotation marks are used, the file is searched for in the source code directory.
For example:
#include <stdio.h>
#include “myutil.h”
Some developers use .hpp extension for header files.
.hpp file extension seems to be convention for C++ header files. Both .h and .hpp are acceptable for both C and C++ headers, but .h should be for C and .hpp for C++
The #define Directive
The #define directive is used to create object-like macros for constants based on values or expressions.
#define can also be used to create function-like macros with arguments that will be replaced by the preprocessor.
Be cautious with function-like definitions. Keep in mind that the preprocessor does a direct replacement without any calculations, which can lead to unexpected results, as demonstrated with the following program:
#include <stdio.h>
#define PI 3.14
#define AREA(r) (PI*r*r)
int main() {
float radius = 2;
printf("%3.2f\n", PI);
printf("Area is %5.2f\n", AREA(radius));
printf("Area with radius + 1: %5.2f\n", AREA(radius+1));
return 0;
}
Before compilation, the preprocessor expands every macro identifier. In this case, every occurrence of PI is replaced with 3.14 and AREA(arg) is replaced with the expression PIargarg. The final code sent to the compiler will already have the constant values in place.
Not what we may expect! However, if you consider that #define works strictly by replacing text, you will see that AREA(radius+1) becomes PIradius+1radius+1, which is 3.142+12+1.
The solution to this is to enclose each parameter in parentheses to obtain the correct order of operations.
For example:
#include <stdio.h>
#define PI 3.14
#define AREA(r) (PI*(r)*(r))
int main() {
float radius = 2;
printf("%3.2f\n", PI);
printf("Area is %5.2f\n", AREA(radius));
printf("Area with radius + 1: %5.2f\n", AREA(radius+1));
return 0;
}
The code produces the output: Area with radius + 1: 28.26.
Formatting Preprocessor Directives
When using preprocessor directives, the # must be the first character on a line. But there can be any amount of white space before # and between the # and the directive.
If a # directive is lengthy, you can use the \continuation character to extend the definition over more than one line.
For example:
#include <stdio.h>
#define VERY_LONG_CONSTANT \
23.678901
#define MAX 100
#define MIN 0
# define SQUARE(x) \
x*x
int main() {
printf("%d\n", VERY_LONG_CONSTANT * SQUARE(2));
return 0;
}
There is a typo(错字) in the output, should be %f instead of %d.
Predefined Macro Definitions
In addition to defining your own macros, there are several standard predefined macros that are always available in a C program without requiring the #define directive:
__DATE__ The current date as a string in the format Mm dd yyyy
__TIME__ The current time as a string in the format hh:mm:ss
__FILE__ The current filename as a string
__LINE__ The current line number as an int value
__STDC__ 1
For example:
#include <stdio.h>
#include <string.h>
int main() {
char curr_time[10];
char curr_date[12];
int std_c;
strcpy(curr_time, __TIME__);
strcpy(curr_date, __DATE__);
printf("%s %s\n", curr_time, curr_date);
printf("This is line %d\n", __LINE__);
std_c = __STDC__;
printf("STDC is %d", std_c);
return 0;
}
Conditional Compilation Directives
The #ifdef, #ifndef, and #undef Directives
The #ifdef, #ifndef, and #undef directives operate on macros created with #define.
For example, there will be compilation problems if the same macro is defined twice, so you can check for this with an #ifdef directive. Or if you may want to redefine a macro, you first use #undef.
The program below demonstrates these directives:
#include <stdio.h>
#define RATE 0.08
#ifndef TERM
#define TERM 24
#endif
int main() {
#ifdef RATE /* this branch will be compiled */
#undef RATE
printf("Redefining RATE\n");
#define RATE 0.068
#else /* this branch will not be compiled */
#define RATE 0.068
#endif
printf("%f %d\n", RATE, TERM);
return 0;
}
Because RATE is defined at the top, only the #ifdef clause will be compiled. The optional #else branch compiles when #ifdef RATE is false during preprocessing.
An #endif is required to close the block of code.
An #elif directive is like an else if and can be used to provide additional alternatives after #else.
Conditional Compilation Directives
Conditional compilation of segments of code is controlled by a set of directives: #if, #else, #elif, and #endif.
#include <stdio.h>
#define LEVEL 4
int main() {
#if LEVEL > 6
/* do something */
#elif LEVEL > 5
/* else if branch */
#elif LEVEL > 4
/* another else if */
#else
/* last option here */
#endif
return 0;
}
There are instances where such conditional compilation can be useful, but this type of code should be used sparingly.
The defined() preprocessor operator can be used with #if, as in:
#if !defined(LEVEL)
/* statements */
#endif
The #if and if statement are not interchangeable. The #if is evaluated using data available to the preprocessor, which then sends only the true branch for compilation.
An if statement uses data provided at runtime with the possibility of branching to any else clause.
#ifdef xy is the same as #if defined (xy); #ifndef is the same as #if !defined (xy);
The ‘#pragma’ directive is the method specified by the C standard for providing additional information to the compiler, beyond what is conveyed in the language itself. The forms of this directive (commonly known as pragmas) specified by C standard are prefixed with STDC. You can learn more on: https://gcc.gnu.org/onlinedocs/cpp/Pragmas.html
Preprocessor Operators
The C preprocessor provides the following operators.
The # Operator
The # macro operator is called the stringification or stringizing operator and tells the preprocessor to convert a parameter to a string constant.
White space on either side of the argument are ignored and escape sequences are recognized.
#include <stdio.h>
#define TO_STR(x) #x
int main() {
printf("%s\n", TO_STR( 123\\12 ));
return 0;
}
The ## Operator
The ## operator is also called the token pasting operator because it appends, or “pastes”, tokens together.
#include <stdio.h>
#define VAR(name, num) name##num
int main() {
int x1 = 125;
int x2 = 250;
int x3 = 500;
printf("%d\n", VAR(x, 3));
return 0;
}
例题
#include <stdio.h>
#define T 42
int main()
{
int T = 8;
printf("%d ", T);
return 0;
}
Compile Error
Think of a macro is copy and pasted throughout the whole code where it appears. So here every T is replaced by 42. Then the line int 42 = 8; does not make any sense