第46课

第46课 - 函数与宏分析

1. 函数与宏

(1)宏是由预处理器直接替换展开的,编译器不知道宏的存在,因此参数无法进行类型检查

        函数是由编译器直接编译的实体,调用行为由编译器决定

(2)多次使用宏会增大代码量,最终导致可执行程序的体积增大,对于嵌入式设备而言,设备资源有限,这个还是比较重要的

        函数是跳转执行的,内存中只有一份函数体存在,不存在宏的问题

(3)宏的效率比函数高,因为宏是文本替换,没有调用开销       //  虽然宏的效率比函数稍高但副作用很大,因此,可以用函数完成的功能绝对不用宏

        函数调用会创建活动记录,效率不如宏

(4)函数可以递归调用,但宏的定义中不能出现递归定义

【函数与宏】

 1 #include <stdio.h>
 2 
 3 // 使用宏将一片内存区域置0
 4 #define RESET(p, len) while(len > 0) 
 5                         ((char *)p)[--len] = 0
 6 
 7 // 使用函数将一片内存区域置0
 8 void reset(void *p, int len)
 9 {
10     while(len > 0)
11         ((char *)p)[--len] = 0;
12 
13 }
14 
15 int main()
16 {
17     int a[5] = {1, 2, 3, 4, 5};
18     int len = sizeof(a); 
19     int i = 0;
20 
21     /*
22         下面的宏和函数都可以实现置0的功能
23         但是假如使用 RESET(10, len),这个在编译期间是不错报错的,宏不会检查参数的类型
24         使用reset(10, len)函数在编译时就会有warning,提示传参类型不符
25     */
26     // reset(a, len);
27     // RESET(a, len);
28 
29     for(i = 0; i < 5; i++) {
30         printf("a[%d] = %d
", i, a[i]);
31     }
32 
33     return 0;
34 }

【宏的副作用】

 1 #include <stdio.h>
 2 
 3 #define _ADD_(a, b) a + b
 4 #define _MUL_(a, b) a * b
 5 #define _MIN_(a, b) ((a) < (b) ? (a) : (b))
 6 
 7 int main()
 8 {
 9     int i = 1;
10     int j = 10;
11 
12     // 预处理结果:printf("%d
", 1 + 2 * 3 + 4);
13     // 预期: (1 + 2) * (3 + 4) ==> 21
14     // 实际: 1 + 2 * 3 + 4 ==> 11
15     printf("%d
", _MUL_(_ADD_(1, 2), _ADD_(3, 4)));  
16 
17     // 预处理结果:printf("%d
", ((i++) < (j) ? (i++) : (j)));
18     // 预期: 1 < 10? 1 : 10 ==> 1
19     // 实际: (i++) < (j) ? (i++) : (b) ==> 2
20     printf("%d
", _MIN_(i++, j));  
21                                     
22     return 0;
23 }

2. 宏的妙用 

 前面讲了宏的很多副作用和缺点,那宏是不是一无是处呢?绝对不是这样的,在C语言中宏有很多妙用。

(1)用于生成一些常规性的代码,比如下面代码中的LOG_INT、LOG_CHAR、LOG_FLOAT、LOG_POINTER

(2)封装函数,加上类型信息,比如下面代码中的MALLOC的实现

【宏的妙用】

 1 #include <stdio.h>
 2 #include <malloc.h>
 3 
 4 #define MALLOC(type, x)  (type*)malloc(sizeof(type) * x)
 5 #define FREE(p)          (free(p), p = NULL)
 6 
 7 #define LOG_INT(i)        printf("%s = %d
", #i,  i)
 8 #define LOG_CHAR(c)       printf("%s = %c
", #c,  c)
 9 #define LOG_FLOAT(f)      printf("%s = %f
", #f,  f)
10 #define LOG_POINTER(p)    printf("%s = %p
", #p,  p)
11 #define LOG_STRING(s)     printf("%s = %s
", #s,  s)
12 
13 #define FOREACH(i, n)    while(1){ int i = 0, l = n;for(i = 0; i < l; i++)    // 使用while是为了定义一个scope,局部变量都在其中
14 #define BEGIN            {
15 #define END              }break;}
16 
17 int main()
18 {
19     int* pi = MALLOC(int, 5);  // ①可以指定数据类型,代码可读性更强 ②返回的void *指针已经进行了强制类型转换
20     char* str = "2020-01-14 22:38:27";
21 
22     LOG_STRING(str);  // str = 2020-01-14 22:38:27
23 
24     LOG_POINTER(pi);  // pi = 0x74f010
25 
26 /*
27     while(1){ int k = 0, l = 5;for(k = 0; k < l; k++)
28     {
29         pi[k] = k + 1;
30     }break;}
31 */
32     FOREACH(k, 5)
33     BEGIN
34         pi[k] = k + 1;  // 对pi对应的堆空间赋值
35     END
36 
37 /*
38     while(1){ int k = 0, l = 5; for(k = 0; k < l; k++)
39     {
40         int value = pi[k];
41         printf("%s = %d
", "value", value);
42     }break;}
43 */
44     FOREACH(k, 5)
45     BEGIN
46         int value = pi[k];  // 遍历pi对应的堆空间
47         LOG_INT(value);    
48     END
49 
50     FREE(pi);   // 安全的释放动态内存
51 
52     LOG_POINTER(pi);    // pi = nil
53 
54     return 0;
55 }
原文地址:https://www.cnblogs.com/shiwenjie/p/11854100.html