Macro definition problem in C++

Detailed explanation of C++ macro definition

portal

 Detailed C++ macro definition

1. Basic usage of #define

    #define is a macro definition command provided in C language. Its main purpose is to provide certain convenience for programmers when programming, and to improve the running efficiency of the program to a certain extent, but students often cannot understand the command's In essence, there is always some confusion here. Misuse of this command during programming makes the operation of the program inconsistent with the intended purpose, or when reading programs written by others, the results of the operation are misunderstood, which is very important for the learning of C language. unfavorable.

1 #define command analysis

1.1 The concept of #define

    The #define command is a macro definition command in C language. It is used to define an identifier as a string, the identifier is called the macro name, and the defined string is called the replacement text.
The command has two formats: one is a simple macro definition, and the other is a macro definition with parameters.

(1) Simple macro definition:
  1. #define <macro name> <string>
  2. 例: #define PI 3.1415926
(2) Macro definition with parameters
 
  1. #define <macro name> (<parameter list>) <macro body>
  2. 例: #define A(x) x
    After an identifier is defined by a macro, the identifier is a macro name. At this time, the macro name appears in the program. Before the program is compiled, the macro name is first replaced with the defined string, which is called macro replacement, and the compilation is performed after the replacement. Macro replacement is a simple replacement .

1.2 When Macro Substitution Occurs

    In order to really understand the role of #define, let's take a look at the processing of the C language source program. When we compile the written source program in an integrated development environment such as Turbo C, it actually goes through several processes of preprocessing, compiling, assembling and linking. Among them, the preprocessor produces the output of the compiler, which realizes the following functions:
(1) The file contains
    can expand the #include in the source program to the file body, that is, find and expand the included .h file to the location of the #include.
(2) Conditional compilation
    The preprocessor includes or excludes a certain part of the source program according to the compilation commands such as #if and #ifdef and the following conditions, and usually converts the excluded statement into a blank line.
(3) Macro expansion
    The preprocessor expands the reference to the macro in the source program file into the corresponding macro definition, that is, the function of #define mentioned in this article, which is completed by the preprocessor.
    The source program processed by the preprocessor is different from the previous source program . The work done at this stage is only pure replacement and expansion, without any calculation function, so as long as you can really understand this when learning the #define command, This prevents misunderstanding and misuse of this command.

2 Analysis of common problems in the use of #define

2.1 Problems in the use of simple macro definitions

    In the use of simple macro definitions, when the string represented by the replacement text is an expression, it is easy to cause misunderstanding and misuse. For example:
   
  1. 例1 #define N 2+2
  2. void main()
  3. {
  4.    int a=N*N;
  5.    printf(“%d”,a);
  6. }
     (1) There is a problem:
 
    There is a macro definition command in this program. The string represented by macro N is 2+2. There is a use of macro N in the program. When students read this program, the easy problem is to solve N first as 2+2=4 , and then use multiplication when calculating a in the program, that is, N*N=4*4=16. In fact, the result of this question is 8. Why is there such a big deviation in the result?

     (2) Problem analysis:
 
    As mentioned in Section 1, macro expansion is done in the preprocessing stage, which treats the replacement text as just a string, and does not perform any calculations. During expansion, it is simply where the macro N appears. Using the string 2+2 instead of N will not add any symbols , so the result after the program is expanded is a=2+2*2+2, after calculation=8, this is the essence of macro substitution, how to write a program to What about completing the operation with a result of 16?

     (3) Solution:
 
  1. /*Write the macro definition as follows*/
  2. #define N (2+2)
  3. /*This can be replaced by (2+2)*(2+2)=16*/

2.2 Problems with macro definitions with parameters

    In the use of macro definitions with parameters, it is very easy to cause misunderstandings. For example, we need to make a macro substitution that can square any number, which requires the use of parameters to replace the parameters in the macro definition with the actual parameters in the program. The average student can easily write it in the following form:
  1. #define area(x) x*x
  2. /* This is easy to cause problems in use, see the following program*/
  3. void main()
  4. {
  5.     int y = area(2+2);
  6.     printf("%d",y);
  7. }
    It is reasonable to say that the parameter given is 2+2, and the result should be 4*4=16, but it is wrong, because the actual result of the program is 8, and it still fails to follow the rules of pure simple replacement, and it is calculated first. Then, in this program, 2+2 is the parameter in the area macro, and it should be used to replace the x in the macro definition, that is, 2+2*2+2=8. Then if we follow the solution in (1) and enclose 2+2, that is, enclose x in the macro body, is it possible? #define area(x) (x)*(x), for area(2+2), replace with (2+2)*(2+2)=16, it can be solved, but for area(2+2)/ What about area(2+2)? Some students give the result as soon as they see this question. Because the numerator and denominator are the same, they are wrong again, or they forget to follow the rule of replacing first and then calculating. This question After replacement, it will become (2+2)*(2+2)/(2+2)*(2+2) that is 4*4/4*4 according to the multiplication and division rules, the result is 16/4*4=4 *4=16, what should be done then? The solution is to add a parenthesis to the entire macro body, ie #define area(x) ((x)*(x)), don't think this is unnecessary, it won't work without it.
    If you want to be able to really use the macro definition well, when reading other people's programs, you must remember to replace all the use of the macro in the program with the string it represents, and don't add any other symbols on your own. Then perform the corresponding calculation, and the running result will not be wrongly written.
     If you program yourself to use macro substitution, when using a simple macro definition, when there is more than one symbol in the string, add parentheses to show priority. If it is a macro definition with parameters, you must give each of the macro body. Arguments are parenthesized, and a parenthesis is added to the entire macro body. Seeing this, I can't help but ask, it is so troublesome and error-prone to define macros, can we abandon it, then let's take a look at the benefits of using macro definitions in C language.
Such as:
  1. #include <iostream.h>
  2. #define product(x)    x*x
  3. intmain()
  4. {
  5.     int i=3;
  6.     int j,k;
  7.     j = product(i++);
  8.     cout<<"j="<<j<<endl;
  9.     cout<<"i="<<i<<endl;
  10.     k = product(++i);
  11.     cout<<"k="<<k<<endl;
  12.     cout<<"i="<<i<<endl;
  13.     return 0;
  14. }
Output the results in sequence:
j=9;i=5;k=49;i=7


3 Advantages of macro definitions

(1) To facilitate program modification

    Using a simple macro definition can use a macro to replace a constant that is often used in the program, so that when the constant is changed, it is not necessary to modify the entire program, only the string defined by the macro can be modified, and when the constant is relatively long, we can It is more convenient to write programs with shorter meaningful identifiers. The constant change we are talking about is not a change during program operation, but a modification during programming. To give a familiar example, pi is a value commonly used in mathematics. Sometimes we use 3.14 to represent it, and sometimes Will use 3.1415926, etc. It depends on the accuracy required for the calculation. If we use it multiple times in a program we compile, then we need to determine a value, which will not change in this run, but maybe later find the accuracy of the program. If there is a change, we need to change its value, which requires modifying all relevant values ​​in the program, which will bring us some inconvenience, but if we use a macro definition and use an identifier instead, only modify the macro when modifying It is enough to define it, and it can also reduce the number of times of entering a value as long as 3.1415926. We can define #define pi 3.1415926 in this way, which not only reduces the input but also facilitates modification. Why not do it?

(2) Improve the operating efficiency of the program

    Using the macro definition with parameters can complete the function of the function call, and can reduce the system overhead and improve the running efficiency. As mentioned in the C language, the use of functions can make the program more modular, easy to organize, and reusable, but when a function call occurs, it is necessary to retain the scene of the calling function, so that the sub-function can return to continue execution after the execution ends. Similarly, it takes a certain amount of time to restore the scene of the calling function after the sub-function is executed. If the sub-function performs many operations, this conversion time overhead can be ignored, but if the sub-function performs less functions, even If only a little operation is done, such as the operation of a multiplication statement, this part of the conversion overhead is relatively large, but this problem will not occur when the macro definition with parameters is used, because it is macro-expanded in the preprocessing stage. No conversion is required at execution time, i.e. it is executed locally. Macro definitions can complete simple operations, but complex operations are still performed by function calls, and the object code space occupied by macro definitions is relatively large. Therefore, it is necessary to decide whether to use the macro definition according to the specific situation when using it.

4 Conclusion

    This paper analyzes the problems that are easy to occur when the macro definition #define in C language is used, and analyzes the processing of #define from the perspective of the C source program processing process, and also expounds its advantages. As long as you can understand the rules of macro expansion and master the use of macro definitions, the source program is replaced in the preprocessing stage, and only the macro names appearing in the program are replaced by the corresponding strings, so that you can fully enjoy the correct use. The convenience and efficiency brought by the use of macro definitions

2. Three special symbols in define: #, ##, #@
 
  1. #define Conn(x,y) x##y
  2. #define ToChar(x) #@x
  3. #define ToString(x) #x
(1) What does x##y mean? Indicates that x is connected to y, for example:
  1. int n = Conn(123,456); /* the result is n=123456;*/
  2. char* str = Conn("asdf", "adf"); /*result is str = "asdfadf";*/
(2) Let's look at #@x , which is actually adding single quotes to x, and the result is a const char. for example:
char a = ToChar(1); the result is a='1';
make an out-of-bounds test char a = ToChar(123); the result is wrong;
but if your parameter exceeds four characters, the compiler will give you an error !
error C2015: too many characters in constant   :P
(3) Finally, look at #x, I guess you understand, he added double quotation marks to x
char* str = ToString(123132); becomes str="123132";
Three, some commonly used macro definitions

1 Prevent a header file from being repeatedly included 
  1. #ifndef BODYDEF_H 
  2. #define BODYDEF_H 
  3.  // header file content 
  4. #endif
 
2 Get a byte or word at the specified address
 
  1. #define MEM_B( x ) ( *( (byte *) (x) ) ) 
  2. #define MEM_W( x ) ( *( (word *) (x) ) )
The usage is as follows:
  1. #include <iostream>
  2. #include <windows.h>
  3. #define MEM_B(x) (*((byte*)(x)))
  4. #define MEM_W(x) (*((WORD*)(x)))
  5. intmain()
  6. {
  7.     int bTest = 0x123456;
  8.     byte m = MEM_B((&bTest));/*m=0x56*/
  9.     int n = MEM_W((&bTest));/*n=0x3456*/
  10.     return 0;
  11. }

3 Get the offset of a field in the structure (struct)

 
  1. #define OFFSETOF( type, field ) ( (size_t) &(( type *) 0)-> field )
     Please refer to the article: Detailed explanation of writing macro definitions: get the offset of a field in the structure (struct type) .

4 Get the number of bytes occupied by the field in a structure 
  1. #define FSIZ( type, field ) sizeof( ((type *) 0)->field )

5 Get the address of a variable (word width) 
  1. #define B_PTR( var ) ( (byte *) (void *) &(var) ) 
  2. #define W_PTR( var ) ( (word *) (void *) &(var) )
6 Convert a letter to uppercase

  1. #define UPCASE( c ) ( ((c) >= ''a'' && (c) <= ''z'') ? ((c) - 0x20) : (c) )
7 Determine whether the character is a 10-digit number

  1. #define DECCHK( c ) ((c) >= ''0'' && (c) <= ''9'')
8 Determine whether the character is a hexadecimal number 
  1. #define HEXCHK( c ) ( ((c) >= ''0'' && (c) <= ''9'') ||((c) >= ''A'' && (c) <= ''F'') ||((c) >= ''a'' && (c) <= ''f'') )
9 One way to prevent overflow
 
  1. #define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))
10 Returns the number of elements in the array 
  1. #define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )
11 Trace debugging with some macros

The ANSI standard specifies five predefined macro names. They are: 
  1. _LINE_ /*(two underscores), corresponding to %d*/
  2. _FILE_ /*corresponds to %s*/
  3. _DATE_ /*corresponds to %s*/
  4. _TIME_ /*corresponds to %s*/

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325914357&siteId=291194637