C语言宏定义相关
前言
C语言所能提供的预处理功能有三种,分别为宏定义、文件包含和条件编译。预处理命令可以改变程序设计环境,提高编程效率, 它们并不是C 语言本身的组成部分, 不能直接对它们进行编译, 必须在对程序进行编译之前, 先对程序中这些特殊的命令进行“预处理” 。经过预处理后, 就将预处理命令转换为实际代码(会将注释代码去掉), 最后再由编译程序对预处理之后的源程序进行编译处理, 得到可供执行的目标代码。
一: 宏定义语法与优缺点
1.1 基本语法
#define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。该命令有两种格式:一种是无参数宏定义,另一种是带参数宏定义。
1)无参数宏定义
#define <宏名> <字符串>
例:
/*无参宏定义*/
#define PI 3.1415926
2)有参数宏定义
#define <宏名> (<参数表>) <宏体>
例:
/*有参数宏定义*/
#define A(x) x+2
一个标识符被宏定义后,该标识符便是一个宏名。这时,在程序中出现的是宏名,在该程序被编译前,先将宏名用被定义的字符串替换,这称为宏替换,替换后才进行编译,宏替换是简单的替换。
1.2 宏定义作用过程
1)文件包含
可以把源程序中的#include 扩展为文件正文,即把包含的.h文件找到并展开到#include 所在处。
2)条件编译
预处理器根据#if和#ifdef等编译命令及其后的条件,将源程序中的某部分包含进来或排除在外,通常把排除在外的语句转换成空行。
3)宏展开
预处理器将源程序文件中出现的对宏的引用展开成相应的宏 定义,即本文所说的#define的功能,由预处理器来完成。
经过预处理器处理的源程序与之前的源程序有所不同,在这个阶段所进行的工作只是纯粹的替换与展开,没有任何计算功能
1.3 宏定义优缺点
优点:
1)方便程序修改
使用简单宏定义可用宏代替一个在程序中经常使用的常量,这样在将该常量改变时,不用对整个程序进行修改,只修改宏定义的字符串即可,而且当常量比较长时, 我们可以用较短的有意义的标识符来写程序,这样更为方便。
相对于全局变量两者的区别如下:
- 宏定义在编译期间即会使用并替换,而全局变量要到运行时才可以。
- 宏定义的只是一段字符,在编译的时候被替换到引用的位置。在运行中是没有宏定义的概念的。而变量在运行时要为其分配内存。
- 宏定义不可以被赋值,即其值一旦定义不可修改,而变量在运行过程中可以被修改。
- 宏定义只有在定义所在文件,或引用所在文件的其它文件中使用。 而全局变量可以在工程所有文件中使用,即宏定义不需要extern。
2)提高程序运行效率
使用带参数的宏定义可完成函数调用的功能,又能减少系统开销,提高运行效率。函数的使用可以使程序更加模块化,便于组织,而且可重复利用,但调用后需要保留调用函数的现场,以便子函数执行结束后能返回继续执行,同样在子函数执行完后要恢复调用函数的现场,这都需要一定的时间,如果子函数完成的功能比较少,甚至于只完成一个加减乘除操作,那么这部分转换开销就相对较大了,但使用带参数的宏定义就不会出现这个问题,因为它是在预处理阶段即进行了宏展开,在执行时不需要转换,即在当地执行。宏定义可完成简单的操作,但复杂的操作还是要由函数调用来完成,而且宏定义所占用的目标代码空间相对较大,所以在使用时要依据具体情况来决定是否使用宏定义。
二:使用宏时的问题总结
2.1 运算符优先级问题
在无参数宏定义的使用中,当替换文本所表示的字符串为一个表达式时,容易引起误解和误用。
例:
#include <stdio.h>
#define N 2+3
int main()
{
int x = N * N;
printf("x = %d", x);
return 0;
}
以上代码运行结果为 x = 11,而不是25,这就是运算符优先级出现了问题,x = 2 + 3 * 2 + 3 = 11,这是因为宏展开是在预处理阶段完成的,这个阶段把替换文本只是看作一个字符串,并不会有任何的计算发生,在展开时是在宏N出现的地方只是简单地使用2+2来代替N,并不会增添任何的符号,那么为了避免这类问题,无论是无参数还是有参数的宏,涉及到运算都应给字符串或是参数加上括号。
2.2 其他问题
1)宏名一般用大写。
2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义。
3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
4)宏定义末尾不加分号。
5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
6)可以用#undef命令终止宏定义的作用域。
7)字符串" "中永远不包含宏(字符串中的宏不会被替换)。
8)宏定义不分配内存,变量定义分配内存。
9)宏定义不存在类型问题,它的参数也是无类型的。
三:宏的特殊符号及惯用法
3.1 宏中包含特殊符号:#、##.
1)#
#是“字符串化”的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串。
2)##
“##”是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。
在普通的宏定义中,预处理器一般把空格解释成分段标志,对于每一段和前面比较,相同的就被替换。但是这样做的结果是,被替换段之间存在一些空格。如果我们不希望出现这些空格,就可以通过添加一些##来替代空格。
3.2 宏定义中do{ }while(0)
采用这种惯用法是为了防范在使用宏过程中出现以下错误:
1)空的宏定义避免warning。
2)存在一个独立的block,可以用来进行变量定义,进行比较复杂的实现。
3)如果出现在判断语句过后的宏,这样可以保证作为一个整体来实现(避免分号吞噬)。
本文详细介绍了C语言的宏定义,包括无参数和有参数宏定义的语法,以及宏定义在文件包含、条件编译和宏展开中的作用。讨论了宏定义的优缺点,如简化代码修改和提高运行效率,同时也指出了使用宏可能遇到的运算符优先级问题和其他注意事项。最后,讲解了宏中的特殊符号和惯用法。

2272

被折叠的 条评论
为什么被折叠?



