C/C ++ macro usage Summary
Macro replacement is the Technical Feature of the C/C ++ series languages. The C/C ++ language provides powerful macro replacement functions. Before the Source Code enters the compiler, first, a module called "Preprocessor" is used to expand the macro Based on the Compilation parameters and actual encoding. Then, the expanded code is officially entered into the compiler, perform lexical analysis and syntax analysis.
There are several types of commonly used macro replicas.
1. Macro Constants
In ACM and other algorithm competitions, the maximum subscript of the array is often given through macro-defined methods to facilitate debugging. For example:
# Deprecision MAX 1000
Int array [Max] [Max]
......
For (INT I = 0; I <Max; I ++)
......
Define a number as a global constant, which is very common in domestic junk textbooks. However, in the classical book "Objective C ++", this practice is not advocated. In this book, we recommend that you replace macro constants with const constants. The macro name is not displayed in the symbol table because the macro reference has been replaced by its actual content during lexical analysis. So if an error occurs, you will see a meaningless number, such as 1000 in the preceding section, rather than a meaningful name, such as Max in the preceding section. Const has its own position in the symbol table, so you can see more meaningful error prompts when an error occurs.
2. Macros used for Conditional compilation
# Define is often used with the # ifdef/# ifndef/defined command for Conditional compilation.
# Ifndef _ header_inc _
# DEFINE _ header_inc _
......
......
# Endif
This type of macro mark is very common in header files to prevent repeated inclusion of header files. You should get into the habit of adding this flag to each header file.
There is also a usage for Conditional compilation
# Ifdef debug
Printf ("{"} debug information \ n ");
# Endif
With the debug macro, We can output the information for auxiliary debugging during code debugging. When the debug macro is deleted, the output statements are not compiled. More importantly, this macro can be defined by compiling parameters. Therefore, by changing the compilation parameter, you can easily add and cancel the definition of this macro, thus changing the compilation result of the Code condition.
# If defined and # If! are recommended for Conditional compilation! Defined is used instead of # ifdef/# ifndef, because the former is more convenient to deal with the situation of multiple branches and more complex conditional expressions. # Ifdef/# ifndef can only process two branches: # ifdef/# ifndef, # else, # endfi; # If defined and # If! Defined can process multiple branches: # If defined/# If! Defined, # Elif defined, # else, # endif. # Ifdef can only determine whether to define the expression, but # If defined can determine whether the value of a complex expression is true.
# If defined (OS _hpux) & (defined (hpux_11_11) | defined (hpux_11_23)
// For HP-UX 11.11 and 11.23
# Elif defined (OS _hpux) & defined (hpux_11_31
// For HP-UX 11.31
# Elif defined (OS _aix)
// For Aix
# Else
...
# Endif
During Conditional compilation, if a file contains too many code for Conditional compilation, some editors may not be able to parse the code well, or the simpler the code, the better. Function-level Conditional compilation can be implemented in two ways:
(1) The same function declaration, the same function definition, and the function body uses Conditional compilation code. This method has a problem. If too much code is compiled by the condition, this function will become very long, which is not conducive to reading and maintenance. one advantage is that it is conducive to the intelligent awareness of the editor, this makes it easier to parse the function name, but with the improvement of the editor function, the difference is not obvious.
(2) According to the compilation conditions, place the Code with the same compilation conditions into a separate file, which is referenced by the Conditional compilation command in the top-level file. The biggest advantage of this method is that programs on different platforms are implemented by different source files, which facilitates the division of labor and cooperation among multiple people, after a part of the code is implemented by one person and tested, the source file can be directly copied. It is very convenient to perform low-level unit tests. Its disadvantage is that it increases the number of files in the directory.
3. macro functions
Syntax of macro functions has the following features:
(1) If a line break is required, a backslash "\" will be added at the end of the line to indicate a line break. The last row of the macro function is not followed by a backslash.
(2) assume that there is a parameter argu and the value is argu, then all argu is directly replaced with argu, # argu is considered as a string, will be replaced with "argu" (with quotation marks ).
(3) because macro functions are directly replaced, no extra points are required at the end of the line when macro functions are called in general.
Macro functions:
1) Avoid function calls and improve program efficiency
Most commonly used judgment functions are the maximum and minimum values. because there are not many function content, if defined as a function, the program efficiency will be significantly reduced when it is called frequently, real macros exchange space efficiency for time efficiency. Take the maximum values of the two values as follows:
# Define max (A, B) (a) <(B )? (B): ())
Defined as a function:
Inline int max (int A, int B)
{
Return a <B? B:;
}
As a template:
Template <typename T>
Inline t Tmax (t a, t B)
{
Return a <B? B:;
}
There are two advantages of using macro functions:
(1) applicable to any type that implements operator <, including custom types;
(2) the highest efficiency. Although the inline prompt is used to define functions or templates as inline, it is only a prompt. Whether the compiler is optimized depends on the implementation of the compiler, macro functions are completely controlled by the Code itself.
Note that, because the essence of macros is direct text replacement, parameters must be enclosed in brackets in the "function body" of macro functions, avoid syntax errors or result errors when parameters are expressions, such:
# Define min (a, B) B <? B:
# Define sum (A, B) A + B
Cout <min (3, 5) <Endl; // syntax error: cout <B <? B: A <Endl;
Int c = sum (a, B) * 2; // The expected value of C is 16, and the actual value is 13.
2) reference the compilation data
Although these functions can achieve better performance by using macro functions, they can be implemented using template functions or common functions instead of macro functions, however, in some cases, you can only use macro-scale. For example, some operations may fail in the program. In this case, you must print the location of the failed code, and you can only use the macro.
# Define show_code_location () cout <__file __< ':' <__line __< '\ N'
If (0! = Rename ("oldfilename", "newfilename "))
{
Cout <"failed to move file" <Endl;
Show_code_location ();
}
Although macros are simple replacements, you can use semicolons to directly write the macro function show_code_location to the definition or call location, but it is best to write the macro to the call location, it looks like a function is called, otherwise it looks like the code is not writable, such:
# Define show_code_location () cout <__file __< ':' <__line __< '\ N'
If (0! = Rename ("oldfilename", "newfilename "))
{
Cout <"failed to move file" <Endl;
Show_code_location ()
}
3) Do-while
The do-while loop control statement is executed at least once (...) When the condition in the body is always 0, the statements in the loop body will be executed only once. The execution effect is the same as that of the Code in the loop body, but they will get more benefits.
# Define swap_int (a, B) Do
{\
Int TMP = ;\
A = B ;\
B = TMP ;\
} While (0)
Int main (void)
{
Int x = 3, y = 4;
If (x> Y)
{
Swap_int (x, y );
}
Return 0;
}
Using the macro definition of the do-while code block, we can not only use swap_int as a function, but also have the following advantages:
(1) You can use local variables in macro definitions;
(2) The macro definition can contain multiple statements, but can be used as a statement, such as the if branch statement in the code, if multiple statements are organized into one code block without do-while, the running result of the program is incorrect and cannot be compiled.
In fact, the swap_int (a, B) We define is equivalent to a function that defines the reference parameter or pointer parameter, because it can change the value of the real parameter. The decltype keyword in C ++ 0x is displayed, because the type of the variable must be determined when a local variable is used in the macro, therefore, this macro can only be used to exchange int-type variable values. If it is changed to another type, a new macro, such as swap_float and swap_char, must be defined, we can define a 10 thousand-level macro.
# Include <iostream>
Using namespace STD;
# Define swap (a, B) Do
{\
Decltype (a) TMP = ;\
A = B ;\
B = TMP ;\
} While (0)
Int main (void)
{
Int A = 1, B = 2;
Float F1 = 1.1f, f2 = 2.2f;
Swap (A, B );
Swap (F1, F2 );
Return 0;
}
The swap "function" implemented by macro implementation is more efficient than using pointer parameters because it uses direct code instead of passing pointer parameters. For some scenarios with obvious efficiency requirements, macro is the first choice.
4. Cancel macro definition
# The UNDEF command is used to cancel the macro defined in # define. After cancellation, you can define the macro again. This command is not used much, because too many # UNDEF statements make code maintenance very difficult. It is generally only used in the configuration file to clear some # define switches to ensure the uniqueness of macro definitions.
// Config. h
# UNDEF has_open_ssl
# UNDEF has_zlib
# If defined (has_open_ssl)
...
# Endif
# If defined (has_zlib)
...
# Endif
Placing the reference to this header file in the first line of all code files ensures that has_open_ssl is not defined. Even if a macro is defined in the compilation options, the # UNDEF command is canceled, in this way, config. H is the only place where the Conditional compilation switch is placed, which is more conducive to maintenance.
5. Notes
1) general macro definition
(1) macro names are generally capitalized.
(2) using macros can improve the versatility and accessibility of the program, reduce inconsistency, reduce input errors, and facilitate modification.
(3) preprocessing is performed before compilation, and one of the tasks of compilation is syntax check. Preprocessing does not perform syntax check.
(4) No plus points at the end of the macro definition;
(5) The macro definition is written outside the curly braces of the function, and the scope is the subsequent program, usually at the beginning of the file.
(6) You can use the # UNDEF command to terminate the macro-defined scope.
(7) macro definitions can be nested
(8) the string "" Never contains macros
(9) macro definition does not allocate memory, variable definition allocates memory.
2) macro definition with Parameters
(1) If the real parameter is an expression, it is prone to problems.
(2) there cannot be spaces between macro names and parameter brackets
(3) macro replacement is only for replacement, without calculation or expression solving
(4) function calls are performed when the program is running after compilation and memory is allocated. Macro replacement is performed before compilation without memory allocation
(5) There is no type or type conversion in the dummy combination of macros.
(6) A function has only one return value. You can use a macro to obtain multiple values.
(7) macro expansion makes the source program longer and function calls will not
(8) macro expansion does not occupy the running time, only the Compilation Time, and function calls occupy the running time (memory allocation, field reservation, value transfer, and return value)
6. About # And ##
In the macro of C language, # is used to stringize the macro parameters following it (stringfication ), simply put, a double quotation mark is added to the left and right sides of the macro variable referenced by the macro variable. For example, the macro in the following code:
# Define warn_if (exp )\
Do {If (exp )\
Fprintf (stderr, "Warning:" # exp "\ n ");}\
While (0)
In actual use, the following replacement process will appear:
Warn_if (divider = 0 );
Replaced
Do {
If (divider = 0)
Fprintf (stderr, "warning" "divider = 0" "\ n ");
} While (0 );
In this way, when divider (divider) is 0, a prompt message will be output on the standard error stream.
# Is called a concatenator to connect two tokens into one. Note that the connected object is a token, not necessarily a macro variable. For example, you want to create an array composed of a menu item command name and a function pointer, and you want to have an intuitive and name relationship between the function name and the menu item command name. The following code is very practical:
Struct command
{
Char * Name;
Void (* function) (void );
};
# Define command (name) {Name, name ##_ command}
// Then you can use some pre-defined commands to easily Initialize an array of command structures:
Struct Command commands [] = {
Command (quit ),
Command (help ),
...
}
The command macro acts as a code generator here, which can reduce the code density to a certain extent and indirectly reduce errors caused by carelessness. We can also use N # symbols to connect n + 1 token. This feature is also not available for # symbols. For example:
# Define link_multiple (A, B, C, D) A ##### B #### C ##### d
Typedef struct _ record_type link_multiple (name, company, position, salary );
// Here the statement is expanded:
// Typedef struct _ record_type name_company_position_salary;
7. Use...
In the C macro, it is called variadic macro, that is, variable parameter macro. For example:
# Define myprintf (templt,...) fprintf (stderr, templt ,__ va_args __)
// Or
# Define myprintf (templt, argS...) fprintf (stderr, templt, argS)
In the first macro, the default macro _ va_args _ is used instead of the variable parameter. In the second macro, we explicitly name the variable parameter as ARGs, so we can use ARGs to represent the variable parameter in the macro definition. Like stdcall in C, the variable parameter must appear at the most of the parameter table. In the macro above, we can only provide the first parameter templt, and the c Standard requires us to write:
Myprintf (templt ,);
. The replacement process is as follows:
Myprintf ("error! \ N ",);
Replace:
Fprintf (stderr, "error! \ N ",);
This is a syntax error and cannot be compiled properly. There are two solutions to this problem. First, the solution provided by gnu cpp allows the preceding macro call to be written:
Myprintf (templt );
It will be replaced:
Fprintf (stderr, "error! \ N ",);
Obviously, compilation errors will still be generated here (not in this example, compilation errors will not be generated in some cases ). In addition to this method, both c99 and gnu cpp support the following macro definition methods:
# Define myprintf (templt,...) fprintf (stderr, templt, ##__ var_args __)
In this case, the # connection symbol is used to remove the comma before when _ var_args _ is empty. The translation process is as follows:
Myprintf (templt );
Converted:
Fprintf (stderr, templt );
In this way, if the templt is valid, no compilation error will be generated. The following lists the error-prone areas in macro usage and the appropriate usage methods.