C language Preprocessing

Source: Internet
Author: User
Tags compact microsoft c

The preprocessing process scans the source code, makes a preliminary conversion, and generates a new source for the compiler. The preprocessing process can be seen before the source code is processed by the compiler.

In the C language, there is no intrinsic mechanism to do the following: include other source files at compile time, define macros, and, depending on the criteria, determine whether or not some code is included in the compilation. To do this, you need to use a preprocessor. Although the majority of compilers now contain preprocessor, they are generally considered to be independent of the compiler. The preprocessing process reads the source code, examines the statements and macro definitions that contain the preprocessing directives, and transforms the source code into a response. The preprocessing process also removes comments and extra white-space characters from the program.

A preprocessing directive is a line of code that begins with a # number. #号必须是该行除了任何空白字符外的第一个字符. #后是指令关键字, there are any number of whitespace characters allowed between the keyword and the # number. The entire line of statements forms a preprocessing directive that makes certain transformations to the source code before the compiler compiles it. The following is a partial preprocessing directive:

Common instruction uses

#空指令, without any effect
#include包含一个源代码文件
#define定义宏
#undef取消已定义的宏
#if如果给定条件为真, the following code is compiled
#ifdef如果宏已经定义, the following code is compiled
#ifndef如果宏没有定义, the following code is compiled
#elif如果前面的 # If the given condition is not true and the current condition is true, compile the following code
#endif结束一个 # If ... #else条件编译块
#error停止编译并显示错误信息

One, the document contains
#include预处理指令的作用是在指令处展开被包含的文件. Inclusions can be multiple, meaning that a contained file can contain other files as well. The standard C compiler supports at least eight nested inclusions.

The preprocessing process does not check whether a file is already contained in the conversion unit and prevents multiple inclusions for it. This allows you to achieve different effects when you include the same header file multiple times, given the conditions at compile time. For example:

#defineAAA
#include "t.c"
#undefAAA
#include "t.c"

To avoid the inclusion of header files that can only be included once, you can control them in the header file with compile-time conditions. For example:
/*my.h*/
#ifndefMY_H
#defineMY_H
......
#endif

There are two formats for including header files in your program:
#include <my.h>
#include "My.h"
The first method is to enclose the file with angle brackets. This format tells the preprocessor to search for included header files in the header files of the compiler's own or external libraries. The second method is to enclose the file in double quotation marks. This format tells the preprocessor to search for the included header file in the source code file of the currently compiled application, and then search the compiler's own header file if it is not found.

The rationale for using two different include formats is that the compiler is installed in a common subdirectory, and the compiled application is under their own private subdirectory. An application contains both the common header file provided by the compiler and the custom private header file. Two different inclusion formats allow the compiler to differentiate a common set of header files in many header files.

two, Macro
A macro defines an identifier that represents a specific content. The preprocessing process replaces the macro identifier that appears in the source code with the value of the macro definition. The most common use of macros is to define a global symbol that represents a value. The second use of a macro is to define a macro with parameters that can be called like a function, but it expands the macro at the calling statement and replaces the formal parameter in the definition with the actual argument at the time of the call.

1. #define指令
#define预处理指令是用来定义宏的. The simplest form of the instruction is: first the deity an identifier, and then give the code represented by this identifier. In the subsequent source code, this is used instead of the identifier. This macro extracts some of the global values that are used in the program and assigns some memory identifiers.
#defineMAX_NUM10
Intarray[max_num];
for (i=0;i<max_num;i++)/*......*/

In this case, for the person reading the program, the symbol Max_num has a specific meaning, which represents the maximum number of elements the array can hold. This value can be used more than once in a program. As a convention, it is customary to always use uppercase letters to define macros, which makes it easy to distinguish between program red macro identifiers and generic variable identifiers. If you want to change the size of the array, you only need to change the macro definition and recompile the program.
The value represented by a macro can be a constant expression that allows the inclusion of a previously defined macro identifier. For example:
#defineONE1
#defineTWO2
#defineTHREE (One+two)
Note that the above macro definition uses parentheses. Although they are not necessary. But in the light of caution, parentheses should be added. For example:
Six=three*two;
The preprocessing process converts the preceding line of code into:
six= (one+two) *two;
If there is no that parenthesis, it is converted into six=one+two*two;
A macro can also represent a string constant, for example:
#defineVERSION "Version1.0copyright (c) 2003"

2. # define directive with parameters
Macros and function calls with parameters look somewhat similar. See an example:
#defineCube (x) (x) * (x) * (x)
can be any numeric expression or even function call in place of the parameter x. Once again, you are reminded of the use of parentheses. Macro expansion is fully enclosed in parentheses, and the parameters are enclosed in parentheses, which guarantees the integrity of the macros and parameters. Look at a usage:
intnum=8+2;
Volume=cube (num);
Expanded to (8+2) * (8+2) * (8+2);
Without those parentheses it becomes 8+2*8+2*8+2.
The following usage is not secure:
Volume=cube (num++);
If cube is a function, the above notation can be understood. However, because Cube is a macro, it can have side effects. The wipes here are not simple expressions, they will produce unexpected results. They unfold after this:
Volume= (num++) * (num++) * (num++);
It is clear that the result is 10*11*12, not 10*10*10;
So how to use cube macro safely? You must move actions that may have side effects to the outside of the macro call:
intnum=8+2;
Volume=cube (num);
num++;

3. #运算符
The # operator that appears in the macro definition converts the argument followed by to a string. Sometimes the # of this usage is called the string operator. For example:

#definePASTE (n) "adhfkj" #n

Main ()
{
printf ("%s/n", PASTE (15));
}
The # operator in the macro definition tells the preprocessor to convert any arguments in the source code that are passed to the macro into a string. So the output should be adhfkj15.

4.# #运算符
# #运算符用于把参数连接到一起. The preprocessor appears in the # #两侧的参数合并成一个符号. Look at the following example:

#defineNUM (a,b,c) a# #b # #c
#defineSTR (a,b,c) a# #b # #c

Main ()
{
printf ("%d/n", NUM (+/-));
printf ("%s/n", STR ("AA", "BB", "CC"));
}

The output of the final program is:
123
Aabbcc
Don't worry, few programmers will know # #运算符 unless you need it or the macro is used exactly as it is at hand. Most programmers never use it.

III. Conditional Compilation directives

Conditional compilation directives will determine which code is compiled and which is not compiled. You can determine the compilation criteria based on the value of the expression or whether a particular macro is defined.

1. #if指令
#if指令检测跟在制造另关键字后的常量表达式. If the expression is true, compile the following code, knowing that #else, #elif或 #endif are present, otherwise it will not compile.

2. #endif指令
#endif用于终止 # if preprocessing directives.

#defineDEBUG0
Main ()
{
#ifDEBUG
printf ("debugging/n");
#endif
printf ("running/n");
}

Since the program definition debug macro represents 0, the # if condition is false and the subsequent code is not compiled until #endif, so the program outputs running directly.
If you remove the # define statement, the effect is the same.

3. #ifdef和 #ifndef
#defineDEBUG

Main ()
{
#ifdefDEBUG
printf ("yes/n");
#endif
#ifndefDEBUG
printf ("no/n");
#endif
}
#ifdefined等价于 #ifdef; #if!defined equivalent to #ifndef

4. #else指令
After #else指令用于某个 the # if directive, the code that follows #else is compiled when the condition of the previous # if directive is not true. #endif指令将中指上面的条件块.

#defineDEBUG

Main ()
{
#ifdefDEBUG
printf ("debugging/n");
#else
printf ("notdebugging/n");
#endif
printf ("running/n");
}

5. #elif指令
#elif预处理指令综合了 the role of #else and # if directives.

#defineTWO

Main ()
{
#ifdefONE
printf ("1/n");
#elifdefinedTWO
printf ("2/n");
#else
printf ("3/n");
#endif
}
The program is well understood and the final output is 2.


6. #error指令

#error指令将使编译器显示一条错误信息, and then stop compiling.

#error message : The compiler stops compiling when it encounters this command and outputs the parameter message. This command is commonly used for program debugging.

The #error指令 syntax format is as follows:
#error token-sequence

Compile the program, whenever you encounter a #error will jump out of a compilation error, since it is a compilation error, what to do it? The goal is to ensure that the program is compiled as you would expect.

Here's an example:
There are often many pre-processing instructions in the program
#ifdef XXX
...
#else

#endif

When the program is relatively large, often some macro definitions are externally specified (such as makefile), or in the system header file specified, when you are not sure whether the current definition of XXX, you can change to the following to compile:

#ifdef XXX
...
#error "XXX has been defined"

#else

#endif

This way, if there is an error at compile time, output XXX has been defined, indicating that macro XXX has been defined.



In fact, when compiling the output compile error message token-sequence, from the convenience of the programmer to check the program errors.

A simple example
#include "stdio.h"
int main (int argc, char* argv[])
{
#define CONST_NAME1 "Const_name1"
printf ("%s/n", const_name1);
#undef const_name1
#ifndef const_name1
#error No defined Constant Symbol const_name1
#endif
......

return 0;
}
Output such as compile information at compile time
Fatal error C1189: #error: No defined Constant Symbol const_name1

7. #pragma指令
#pragma指令没有正式的定义. The compiler can customize its purpose. A typical use is to prohibit or allow some annoying warning messages.


In all pre-processing directives, the #pragma directive may be the most complex, and its role is to set the state of the compiler or to instruct the compiler to complete certain actions.
#pragma指令对每个编译器给出了一个方法, given the unique characteristics of the host or operating system, while maintaining full compatibility with the C and C + + languages.
By definition, the compilation instructions are proprietary to the machine or operating system and are different for each compiler.
The format is generally: #pragma para
Where para is the parameter, here are some common parameters.

(1) Message parameter

The message parameter is one of my favorite parameters that can output the appropriate information in the Compile Information Output window.
This is very important for the control of source code information. It is used in the following ways:
#pragma message ("text")
When the compiler encounters this instruction, it prints the message text in the compilation Output window.
When we define a number of macros in the program to control the source code version, we may forget that we have the correct settings for these macros.
At this point we can use this command to check at compile time. Let's say we want to determine if we have a macro that defines _x86 somewhere in the source code.
You can use the following method:
#ifdef _x86
#pragma message ("_x86 macro activated!")
#endif
After we have defined the _X86 macro, the application will display the "_86 macro activated!" in the compiled Output window at compile time.
We will not scratching because we do not remember some of the specific macros we have defined.

(2) Another pragma parameter that is used more is code_seg

Formats such as:
#pragma code_seg (["Section-name" [, "Section-class"]]
It is able to set the code snippet where the function code is stored in the program, which is used when we develop the driver.

(3) #pragma once (more commonly used)

As long as this command is added at the very beginning of the header file, it is guaranteed that the header file will be compiled once, and this instruction is actually in VC6.
But considering compatibility doesn't have much to do with it.


(4) #pragma hdrstop

This means that the precompiled header file ends, and the subsequent header files are not precompiled. BCB can precompile the header file to speed up the link,
However, if all header files are precompiled and may account for too much disk space, use this option to exclude some header files.
Sometimes there are dependencies between units, such as cell A depends on unit B, so unit B is compiled before unit A.
You can use #pragma startup to specify the compilation priority, if the #pragma package (smart_init) is used,
BCB will be compiled according to the priority size.


(5) #pragma resource "*.DFM"

Indicates that the resources in the *.DFM file are added to the project. Include form in *.DFM
The definition of the appearance.


(6) #pragma warning (disable:4507; once:4385; error:164)

Equivalent to:
#pragma warning (disable:4507 34)//Do not display 4507 and 34th warning messages
#pragma warning (once:4385)//No. 4385 warning information is reported only once
#pragma warning (error:164)//Put the 164th warning message as an error.

This pragma warning also supports the following formats:
#pragma warning (push [, N])
#pragma warning (POP)
Here n represents a warning level (1---4).
#pragma warning (push) to save an existing warning state for all warning messages.
#pragma warning (push, N) holds the existing warning state for all warning messages and sets the global warning level to N.
#pragma warning (pop) pops the last warning message to the stack and cancels all changes made between the stack and the stack. For example:
#pragma warning (push)
#pragma warning (disable:4705)
#pragma warning (disable:4706)
#pragma warning (disable:4707)
//.......
#pragma warning (POP)
At the end of this code, all warning messages (including 4705,4706 and 4707) are re-saved.


(7) #pragma comment (...)

This directive places a note record into an object file or executable file.
The commonly used LIB keyword can help us to connect to a library file. Such as:
#pragma comment (lib, "Comctl32.lib")
#pragma comment (lib, "Vfw32.lib")
#pragma comment (lib, "Wsock32.lib")



Each compiler can use the #pragma directive to activate or terminate some of the compilation features supported by the compiler.

For example, for the Loop optimization feature:
#pragma loop_opt (ON)//Active
#pragma loop_opt (off)//termination

Sometimes, there are functions in the program that cause the compiler to emit warnings that you are familiar with and want to ignore.
such as "Parameter xxx is never used in function xxx", you can do this:
#pragma warn-100//Turn off the warning message for warning #100
int Insert_record (REC *r)
{/* Function body */}
#pragma warn +100//Turn The warning message for warning #100 back on
The function generates a warning message with a unique signature of 100, so that the warning can be temporarily terminated.

Each compiler has a different implementation of #pragma, which is effectively in a compiler that is almost invalid in another compiler. Can be viewed from the compiler's documentation.

Supplemental--#pragma pack and memory alignment issues


Many real computer systems restrict the location of basic types of data in memory, and they require that the value of the first address of the data be a number k
(usually it is a multiple of 4 or 8), which is called memory alignment, and this k is called the number of Zimo (alignment modulus) for that data type.

The Microsoft C compiler (Cl.exe for 80x86) under the Win32 platform uses the following alignment rules by default:
The Zimo number of any basic data type T is the size of T, which is sizeof (t). For example, for a double type (8 bytes),
The address that requires this type of data is always a multiple of 8, and the Char type data (1 bytes) can start at any address.

GCC under Linux pursues a different set of rules (found in the data, not verified, such as errors please correct):
Any 2-byte size (including single-byte?) The data type (such as short) of the Zimo number is 2, and all other data types more than 2 bytes
(for example, long,double) all use 4 for the number of Zimo.

ANSI c specifies that the size of a struct type is the sum of the size of all its fields and the size of the fill area between fields or the end of a field.
The fill area is the extra space allocated to the struct for the structure field to meet the memory alignment requirements. So what are the alignment requirements of the structure itself?
Yes, the ANSI C standard specifies that the alignment requirements of the struct type cannot be more stringent than the one that is most stringent in all of its fields.


How to use the alignment options in C + +

The compilation options in VC6 are/zp[1|2|4|8| /ZP1 represents a 1-byte boundary alignment, and the corresponding/ZPN represents an n-byte boundary alignment.
N-byte boundary alignment means that the address of a member must be arranged on an integer-multiple address of the member's size or an integer-multiple address of N, taking the minimum value from them.
That is
Min (sizeof (member), N)

In fact, a 1-byte boundary alignment also indicates that there are no voids between struct members.
The/ZPN option is applied to the entire project, affecting all the constructs that participate in the compilation.
To use this option, you can open the Project Properties page in VC6, C + + pages, select the code Generation category, and select the struct member alignment.

To use the alignment options specifically for certain structure definitions, you can use the #pragma pack compilation directives:


(1) #pragma pack ([n])

This instruction specifies the compact alignment of the structure and the union members. While the structure of a complete conversion unit and the combined compact alignment are set by the/zp option.
Compact alignment is set in the data Description layer with pack compilation instructions. The compilation instruction takes effect at the first structure or joint description after it appears.
The compilation indicates that the definition is invalid.
When you use #pragma pack (n), here n is 1, 2, 4, 8, or 16.
Each struct member after the first struct member is stored within a smaller member type or an n-byte boundary.
If you use the #pragma pack without parameters, the struct member is compact to the value specified with/zp. The default/zp compact value is/ZP8.


(2) The compiler also supports the following enhanced syntax:
#pragma pack ([[[{push | pop},] [identifier,]] [n])

If different components use the pack compilation instructions to specify different compact alignments, this syntax allows you to combine program components into a single unit of conversion.
Each occurrence of the pack compilation indicator with the push parameter stores the current compact alignment in an internal compiler stack.
The parameter table for compiling instructions is read from left to right. If you use push, the current compact value is stored;
If you give a value of N, the value becomes the new compact value. If you specify an identifier, that is, you select a name,
The identifier will be associated with this new compact value.

Each occurrence of a pack compilation indicator with a POP parameter retrieves the value of the top of the internal compiler stack and causes the value to be a new compact alignment value.
If you use a pop parameter and the internal compiler stack is empty, the compact value is the value given by the command line, and a warning message is generated.
If you use pop and specify a value of N, the value becomes the new compact value. If you use P O p and specify an identifier,
All values stored in the stack are removed from the stack until a matching identifier is found, and the compact value associated with the identifier is also removed from the stack.
Also this compact value that exists only before the identifier into the stack becomes the new compact value. If no matching identifiers are found, the
A compact value that will be set using the command line, and a first level warning will be generated. The default compact alignment is 8.

The new enhancements to the pack compilation instructions let you write a header file that ensures that before and after encountering the header file
The compact value is the same.


(3) Stack memory alignment

The alignment of the stack in VC6 is not affected by the structure member alignment options. It is always aligned and aligned on a 4-byte boundary.

8. #line指令

#line指令可以改变编译器用来指出警告和错误信息的文件号和行号.

Add:

Preprocessing is the work done prior to the first lexical scan and parsing of the compilation. To be blunt, the preprocessing part is processed before the source file is compiled, and then the processed code is compiled. The advantage of this is that the processed code will become very short.
About the file contained in the preprocessing command (#include), the macro definition (#define), the book has a detailed description, here is not detailed. This is mainly a description of conditional compilation (#ifdef, #else, #endif, #if等). Here are 3 things:

1: Situation 1:
#ifdef _XXXX
... Program Section 1 ...
#else
... Program Section 2 ...
#endif
This indicates that if the identifier _xxxx has been defined by the # define command, the program Segment 1 is compiled, otherwise the program segment 2 is compiled.

Cases:
#define NUM
.............
.............
.............
#ifdef NUM
printf ("Before NUM has been defined!") :)/n ");
#else
printf ("Before Num has no definition!") :(/n ");
#endif
}
If the program starts with a # define NUM line, that is, NUM has a definition, and when it encounters the #ifdef num below, of course executes the first printf. Otherwise a second printf will be executed.
I think that, with this, it is very convenient to turn on/off a specific function of the whole program.

2: Situation 2:
#ifndef _xxxx
... Program Section 1 ...
#else
... Program Section 2 ...
#endif
The #ifndef is used here, which means if not def. Of course it is the opposite of #ifdef (if the identifier _xxxx is not defined, then execute program segment 1, otherwise execute program segment 2). The example is not to be lifted.

3: Situation 3:
#if constants
... Program Section 1 ...
#else
... Program Section 2 ...
#endif
This indicates that if the constant is true (not 0, whatever the number, as long as it is not 0), execute program segment 1, otherwise execute program segment 2.
In my opinion, this method can add the test code. When you need to open the test, as long as the constant quantity of 1 is good. Instead of testing the time, as long as the constant quantitative 0.

C language Preprocessing

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.