C language advanced articles: macros

define essentially replaces the text content at compile time , and because it can replace parameters, macros appear.

#definereplace text

grammar:#define name stuff

As an example

#define MAX 1000
#define reg register          //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;)     //用更形象的符号来替换一种实现
#define CASE break;case        //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
                          date:%s\ttime:%s\n" ,\
							__FILE__,__LINE__ ,\
							__DATE__,__TIME__ )   

Our C language programmers have a tacit understanding. We generally use all uppercase#define functions for the definitions. Generally , the names of functions are not all capitals, generally the first letter is capitalized or other parts are capitalized. Of course, our macro definitions will not be all capitalized if they are functions.

#define定义宏

#define允许把参数替换到文本中,这种实现通常被称为宏,或定义宏

definename (symbols separated by commas) stuff

Example:

#include<stdio.h>
#define SQUARE(x) x*x
int main()
{
    
    
	printf("%d", SQUARE(3));
	return 0;
}

![[202201210943514.png]]

It is worth noting that the
above macro actually has a big drawback.

We illustrate with the following code:

#include<stdio.h>
#define SQUARE(x) x * x
int main()
{
    
    
	printf("%d", SQUARE(3+2));
	return 0;
}

![[202201210945131.png]]

This is not the answer we want. We want to get 5*5, but the formula given by this macro definition gives us 11. This is because we did not consider the priority of the operation when we defined the macro .

First of all, our macro will directly replace the code with the code during the compilation phase. This time the macro will replace the content of the printf function.SQUARE(3+2)替换成了3+2*3+2这样我们就得到了11的值。

So be sure to add parentheses when using macros! Add parentheses!

#include<stdio.h>
#define SQUARE(x) ((x) * (x))
int main()
{
    
    
	printf("%d", SQUARE(3+2));
	return 0;
}

The above situation will not occur.

** #define substitution rules**
There are several steps involved when expanding #define-defined symbols and macros in a program.

  1. When calling the macro, the arguments are first checked to see if they contain any symbols defined by #define. If so, they are replaced first .
  2. The replacement text is then inserted into the program in place of the original text. For macros, parameter names are replaced by their values.
  3. Finally, the resulting file is scanned again to see if it contains any symbols defined by #define. If so, repeat the above process.

Notice:

  1. Other #define-defined symbols may appear in macro parameters and #define definitions. But for macros, recursion cannot occur .
  2. When the preprocessor searches for symbols defined by #define, the contents of string constants are not searched .
  3. Arguments passed to function macros cannot contain tokens that appear to be preprocessing directives.
#define A 100
printf("A"); //不行
"#define A 100" //不行
string macro constant
//第一个宏,字符串没有带双引号,直接报错
#define PATH1 hello!

//第二个宏,字符串带上双引用,有告警,能够编译通过。不过windows中路径分割符需要\\,输出乱码,改过之后,正常  
//双斜杠是为了防止转义成别的字符
#define PATH1 "C:\\Users\\Desktop\\C语言"  

Note: You can use backslashes for line continuation.

Use macro definitions as comment symbols

Directly to the conclusion: ** is to perform de-comment first, and then perform macro substitution. **
(you can try it on linux platform)

//当前,我们用BSC充当C++风格的注释  
#define BSC //  
int main()  
{
    
      
  BSC printf("hello world\n");  
  return 0;  
}

//结果
hello world
//我们发现,并没有报错  
//甚至可以运行,但是目标代码并没有被注释掉

//查看预处理过程  
int main()  
{
    
      
  printf("hello world\n");  
  return 0;  
} 

If, macro substitution is performed first, then the code obtained first should be.

int main()  
{
    
      
  //将BSC替换成为‘//’  
  // printf("hello world\n");  
  return 0;  
} 
//再执行去注释,那么代码最终的样子,应该是  
int main()  
{
    
      
  return 0; //printf被注释掉  
}

But that's not the case. So it is first to perform de-comment, and then to perform macro substitution .

//先去掉宏后面的//,因为是注释  
#define BSC // //最终宏变成了#define BSC  
int main()  
{
    
      
  BSC printf("hello world\n"); //因为BSC是空,所以在进行替换之后,就是printf("hello world\n");  
  return 0;  
}

Another example:

#define BSC //
#define BMC /*  
#define EMC */

//注意:EMC已经被是先被注释掉,BMC成为空

int main()  
{
    
      
  BSC printf("hello world\n");  
  BMC printf("hello bit\n"); EMC  
  return 0;  
}

//预处理之后
int main()  
{
    
      
  printf("hello world\n");  
  printf("hello bit\n"); EMC  
  return 0;
  //在宏定义那里,EMC已经被注释掉了,所以这里无法替换
}

spaces in macro definitions

#define INC(a) ((a)++) //定义不能带空格  
int main()  
{
    
      
  int i = 0;  
  INC (i); //使用可以带空格,但是严重不推荐(这种地方还是不要特立独行的好)
  printf("%d\n", i);  
}

#and##

Before introducing these two, we need to know first

	char* p = "hello ""bit\n";
	printf("hello"" bit\n");
	printf("%s\n", p);

They all print "hello bit" because the strings are automatically concatenated .

  • automatic concatenation of strings
  • # Turn a macro parameter into the corresponding string .
  • ## Combine two symbols into one symbol

Introduce #the operation directly on the example:

#define PRINT(x) printf("The "#x" value is %d",x)
int main()
{
    
    
	int a = 10;
	PRINT(a);
	return 0;
}

![[Pasted image 20220227161626.png]]
This way #x in the define is the same as "x".

#define PRINT(x,y) printf("The "#x" value is" #y,x)

The role of ## combines the symbols on both sides into one symbol .

#define ADD_TO_SUM(num, value) sum##num += value
int main()
{
    
     
	int sum1 = 20;
	int sum2 = 10;
	ADD_TO_SUM(1, 20);//sum1+=20
	printf("%d", sum1);
	return 0; 
}

![[Pasted image 20220227161705.png]]
Note: Such a connection must produce a valid identifier. Otherwise the result is undefined.

Macro parameters with side effects

x+1;//不带副作用
x++;//带有副作用

Do not use parameters with side effects in macros.

The following example

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
int main()
{
    
    
	int x = 5;
	int y = 8;
	int z = MAX(x++, y++);
	printf("x=%d y=%d z=%d\n", x, y, z);
	return 0;
}

![[Pasted image 20220227162001.png]]
Otherwise, it will always bring unwanted results because y is done twice ++ and x is done once ++ z gets the value of y++ which is 9 Of course we know the macro MAX What can be easily reversed, but I don't know what bugs will appear in large-scale projects in the future.

undef

remove macro definition

#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。

![[Pasted image 20220227163330.png]]

2 questions:

  1. Can macros only be defined above main?
  2. What is the valid scope of macros within a source file?
#define M 10  
int main()  
{
    
     
	# define N 100  
printf("%d, %d\n", M, N);  
return 0;  
}

![[Pasted image 20220227162302.png]]
This is also possible.
Conclusion: The macro definition can be anywhere, and it is generally placed at the top.

how about this?
![[Pasted image 20220227162715.png]]
This will report an error.
![[Pasted image 20220227163129.png]]
Conclusion: The valid range of the macro is valid from the definition down, and invalid before.

one question

![[Pasted image 20220227063358.png]]
![[Pasted image 20220227153731.png]]
seems to be fine now, but what if
![[Pasted image 20220227153907.png]]
?
report an error directly.
![[Pasted image 20220227154019.png]]
else the problem of hanging in the air, you can understand it by looking at the picture √

Conclusion:
Macros defining multiple statements may have some problems in certain scenarios.

Option 1:
If and else with curly braces.
But not all users have the habit of putting curly braces. For the maintainability of the code, we have to modify it.

Option 2:
Actively add curly braces to the macro.
But an error is reported, the reason:
![[Pasted image 20220227155349.png]]
has one more;
we can't force the programmer not to add a semicolon, so the second plan is invalid.

Option 3:
dowhile structure.

#define INIT_VALUE(a,b) \  
        do{
    
    a = 0;b = 0; }while(0)
   
int main()  
{
    
      
  int flag = 0;  
  scanf("%d", &flag);  
  int a = 100;   
  int b = 200;  
  printf("before: %d, %d\n", a, b);  
  if(flag){
    
      
    INIT_VALUE(a,b);  
  } 
  else{
    
      
    printf("error!\n");  
  }
  printf("after: %d, %d\n", a, b);  
  return 0;  
}

Conclusion: When we need macros for multi-statement replacement, the do-while-zero structure is recommended. Of course, if possible, macros are recommended to be used sparingly.

macro vs function

Macros are usually used to perform simple operations. For example, find the larger of two numbers.

#define MAX(a, b) ((a)>(b)?(a):(b))

So why not use functions to accomplish this task?
There are two reasons:

  1. The code for calling and returning from the function may take more time than actually doing this small computational work . So macros outperform functions in terms of program size and speed .
  2. More importantly, the parameters of the function must be declared as specific types. So functions can only be used on expressions of the appropriate type. Conversely, how can this macro be applied to integers, long integers, floating point types, etc. that can be used for > to compare types. Macros are type-independent .

Of course, functions also have disadvantages compared to macros:

  1. Every time a macro is used, a copy of the macro definition code is inserted into the program. Unless the macro is relatively short, it can significantly increase the length of the program .
  2. Macros cannot be debugged .
  3. Macros are not rigorous enough because they are type-independent.
  4. Macros can cause problems with operator precedence, making procedures prone to errors.

Macros can sometimes do things that functions can't. For example: macro parameters can have types, but functions cannot.

#define MALLOC(num, type)\  
(type *)malloc(num * sizeof(type))  
  
//使用  
MALLOC(10, int);//类型作为参数  
//预处理器替换之后:  
(int *)malloc(10 * sizeof(int));
Attributes #define define macro function
code length Macro code is inserted into the program each time it is used. Except for very small macros, the length of the program can grow substantially. Function code appears in only one place; every time the function is used, the same code in that place is called
execution speed faster There is additional overhead of calling and returning the function, so it is relatively slow.
operator precedence Macro parameters are evaluated in the context of all surrounding expressions. Unless parentheses are added, the precedence of adjacent operators may have unpredictable consequences, so it is recommended that macros be written with more parentheses . A function parameter is evaluated only once when the function is called , and its result value is passed to the function. Expression evaluation results are more predictable.
Parameters with side effects Parameters may be substituted in multiple places in the macro body, so parameter evaluation with side effects may produce unpredictable results. Function parameters are only evaluated once when the parameters are passed , and the result is easier to control.
Parameter Type The parameters of a macro are independent of type, and can be used with any parameter type as long as the operation on the parameter is legal . The parameters of the function are related to the type, if the types of the parameters are different, different functions are needed, even if the tasks they perform are different.
debugging Macros are inconvenient to debug Functions can be debugged statement by statement
recursion Macros cannot be recursive Functions can be recursive
naming convention

In general, the syntax for using macros for functions is similar. So language itself cannot help us distinguish between the two.

Then we usually have a habit:
to capitalize the macro name.
Do not use all uppercase function names.

Guess you like

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