宏定义与分析
宏定义与分析:
我们都数值宏定义通过define来定义。#define 定义的变量区别于const等定义变量的方式,其本质时字面量,并不会占用内存空间。
define定义的函数:
define 定义的函数比常规函数定义更加强大,也方便。
#define sum(a, b) (a)+(b) #define _MIN_(a,b) ((a) < (b) ? (a) : (b)) #define _DIM_(a) sizeof(a) / sizeof(*a)
宏定义比常规函数更容易出错:
int a = 1; int b = 2; int c[5] = {0}; int s = _SUM_(a,b) * _SUM_(a,b) // 常规想法是 3*3 = 9,而实际输出为 1+2*1+2 = 5; int s1 = _DIM_(c) // 输出数组长度 5
首先我们先讲宏定义的强大的地方,如果按照 “ sizeof(c) / sizeof(int) ”的方式计算数组长度,正常函数是无法计算的,返回的均为1。其原因是数组作为实参传入函数内部,其本质传入的为数组的首地址,所以sizeof(c)为首地址的大小而非数组大小。而宏定义内的数组参数则可以作为正常数组大小来计算,这就是宏定义强大的地方之一。
其次,我们在使用宏定义做复杂的运算时,一定要注意元素符号的优先级,比如上面两个累加和相乘, * 的优先级要高于 + ,所以才会出现1+2*1+2 的情况,最好的方法是用()括起来。
那么相比于定义的add()函数,为什么宏定义会出现这种情况呢?
我们都知道编译器会对代码的逻辑以及语法进行检查,以及后续的计算。而宏定义在编译器编译之前,已经被预处理器进行处理了。这时候预处理器并不知道后面所跟的内容(*_SUM_(a,b)),他只会去处理分析宏定义的逻辑,代码的逻辑语法并不会去分析处理。
宏定义没有任何的“调用”的开销,在实际函数定义中,通常会把实参作为形参来处理。但相比于函数,宏定义并不能实现递归逻辑。
宏定义的常量是否有作用域
// 在一个函数内定义两个宏定义,在另一个函数中使用,这种方法是否是可行的 void def(){ #define PI 3.1415926 #define ARR(r) r*r*PI } double area(double r){ retrun ARR(r); }
我们都知道,函数内部定义的变量的作用域只在函数内,不能被其他函数所调用。而宏定义是在编译前预处理器处理的,所以编译器并不知道宏定义的位置,所以与函数外定义的宏是等同的,但是在实际编程规范里还是要写在文件开头位置。
C语言内置宏:
// 举一个例子 file.c #define MALLOC(type, n) (type*)malloc(sizeof(type) * n) #define FREE(p) {free(p), p=NULL} // 释放p内存,并指向NULL #define LOG(s) printf("[%s][%s][%s], %s/n", __DATA__, __FILE__, __LINE__, s) #define FOREACH(i, n) for(i = 0; i<n; i++) #define START { #define END } int main() { LOG("start to run main"); int x = 0; int *p = MALLOC(int, 5); // 宏定义可以讲数据类型作为实参传入函数。 FOREACH(x, 5) START p[x] = x; END FOREACH(x, 5) START printf("%d", p[x]); END FREE(p); LOG("stop to run main"); }
// 输出结果 [Apr 2 2023][test.c][13], start to run main 0 1 2 3 4 [Apr 2 2023][test.c][27], stop to run main