目录
预定义符号:
使用:
结果:
预编译前后对比:
#define定义常量:
基本语法:
举例1:
结果:
预编译前后对比:
举例2:
预编译前后对比:
注意事项:
#define定义宏:
下⾯是宏的申明⽅式:
举例:
使用:
预编译前后对比:
结果:
注意事项:
1.参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。
举例:
正确的:
错误的:
2.不够严谨的写法导致的失误:
举例:
结果:
使用预编译进行查看替换的情况:
所以严谨的写法应该是:
带有副作用的宏:
举例:
当我们使用a++和b++这种带有永久性的效果时:
预编译前后对比:编辑
结果:
分析:
宏的替换规则:
宏和函数的对比:
优势:
函数和宏的过程:
劣势:
宏做得到,函数做不到:
什么时候使用宏?
总结图:
预定义符号:
C语⾔设置了⼀些预定义符号,可以直接使⽤,预定义符号也是在预处理期间处理的。
__FILE__ //进⾏编译的源⽂件
__LINE__ //⽂件当前的⾏号
__DATE__ //⽂件被编译的⽇期
__TIME__ //⽂件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
使用:
int main()
{ printf("%s\n", __FILE__);printf("%d\n", __LINE__);printf("%s\n", __DATE__ ;printf("%s\n", __TIME__);
}
结果:
预编译前后对比:
#define定义常量:
基本语法:
#define name stuff
举例1:
int main()
{int a = Mprintf("%d\n",M);printf("%s\n",STR);return 0 :
}
结果:
预编译前后对比:
举例2:
#define forever for( ; ; )
int main()
{int a = M;printf("%d\n",M);printf("%s\n",STR);forever;return 0;
}
结果是无限循环
预编译前后对比:
注意事项:
在define定义标识符的时候,不要在最后加上 ;
#define定义宏:
#define 机制包括了⼀个规定,允许把参数替换到⽂本中,这种实现通常称为宏(macro)或定义宏 (define macro)。
下⾯是宏的申明⽅式:
#define name( parament-list ) stuff
举例:
#define SOAURE(X) X*X
使用:
#define SOAURE(X) X*X
int main()
{int a = 5;printf("%d\n",SQUARE(a));return 0;
}
预编译前后对比:
结果:
注意事项:
1.参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。
举例:
正确的:
#define SQUARE(X) X*X
错误的:
#define SQUARE (X) X*X
错误在于SQUARE举例(x)之间有一个空格,正常来说是不因该又间隔的,这二者应该紧贴在一起,也就是换在原型上来说,name应该和 ( parament-list )紧贴着!
2.不够严谨的写法导致的失误:
举例:
#define SOAURE(X) X*X
int main()
{int a = 5;printf("%d\n",SQUARE(a+2));return 0;
}
按照我们的想法因,再进行#define替换后,应该是(a+2)*(a+2),因为a=5,所以最后的结果因该是7*7=49,但是结果并不是如此。
结果:
使用预编译进行查看替换的情况:
并不是我们想象中的 (a+2)*(a+2),而是a+2*a+2,也就是5+2*5+2=5+10+2=17
所以严谨的写法应该是:
#define SQUARE (X) ((X)*(X))
以此避免因为不严谨而带来的错误。
带有副作用的宏:
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险导致不可预测的后果。
副作用就是表达式求值的时候出现的永久性效果。
举例:
#define MAX(a, b) ((a)>(b)?(a):(b))
当我们使用a++和b++这种带有永久性的效果时:
int main()
{int a = 15;int b = 9;int m = MAX(a++, b++);printf("%d\n",m);printf("a=%d b=%d\n",a,,b);return 0;
}
预编译前后对比:
结果:
分析:
通过宏的替换,将 int m = MAX(a++, b++);替换为了int m = ((a++)>(b++)?(a++):(b++));
也正是因为替换,导致了++的不确定性。
- 1.因为a=15,再(a++)>(b++)中进行了使用,使用完后 a和b都因为++加上了1
- 2.因为再(a++)>(b++)中a比b大,所以执行?(a++):(b++));因为是a大,所以只执行(a++),所以最后答案输出给m的是16,但是输出完后,进行++执行,所以a又继续+1,得到17
宏的替换规则:
通过上诉我们得知了,宏是先把对应的内容替换掉,再进行运算。
而在程序中扩展#define定义符号和宏时,需要涉及⼏个步骤。
- 在调⽤宏时,⾸先对参数进⾏检查,看看是否包含任何由#define定义的符号。如果是,它们⾸先 被替换。
- 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果⽂件进⾏扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上 述处理过程。
注意事项:
- 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
如图所示:printf("M = %d\n",M);中的M =%d\n的M并未被替换 。
宏和函数的对比:
如上述代码所示:宏和函数,那一个更好呢?
其实,在函数和宏中,其实对于较为简单的代码来说,宏是一种更好的选择,也是宏的一种优势。
优势:
- 函数是进行调用的,而宏是直接进行替换的,对于调用再编译器中是有一个过程的,而对于替换而言,这种替换的过程比调用要简单的多
#define MAX(a,b) ((a)>(b)?(a):(b))
int m = MAX(100,101);
int m = ((100) > (101) ? (100) :(101));
再预编译中,上诉的两个代码其实是一个东西, 因为宏的作用下,编译器直接将第一行代码变成了第二行代码,或者说,直接把第一行代码看出第二行代码对待,这种方式是宏特有的替代。
也正是因为这种直接替代,使得宏在简单的代码中比调用函数的运行速度更快。
而调用函数:
在使用调用函数正式调用之前,会执行一堆代码指令,当这一堆指令完成后才开始进行调用函数。
这也是宏比函数快的原因。
函数和宏的过程:
- 宏因为是替换,所以宏的参数是没有类型的。
这是比函数快的原因,也是一个不确定的因素。
劣势:
- 因为每一次使用宏,就会有一段代码被宏替代,若设置的宏较长,那么整体的代码会被宏拉长,反倒是函数因为可以多次使用会更简洁。
- 关于调试,当程序运行的时候,预处理就以及结束了,看到的宏的代码已经被预编译了,看不到预编译之前的宏,所以也可以说,宏不能逐步调试。
- 其次最后,因为参数没有类型,所以不是非常稳定。
- 以及,优先级问题,也就是之前设置的宏不够严谨导致参数的运算过程中符号优先级的出错。
#define SOAURE(X) X*X//不够严谨的写法
宏做得到,函数做不到:
当我们开辟空间的时候,如果怕麻烦的花可以使用宏来定义。
int *p = (int*)malloc(10*sizeof(int));
使用宏后:
#define MALLOC(n,type) (typex)malloc(n*sizeof*(type))
利用了宏是可以把类型当参数进行传值替代的特点,这就是函数做不到的地方。
#define MALLOC(n,type) (typex)malloc(n*sizeof*(type))
int main()
{int* p = MALLOC(10,int);return 0;
}
什么时候使用宏?
- 计算逻辑如果比较简单,就可以使用宏
- 计算逻辑比较复杂,建议使用函数