C 语言常用的预处理指令之一丨宏定义

​C 语言的源代码到生成可运行的程序(.exe)文件,需要经过四个阶段,分别是:预处理、编译、汇编以及链接。其中预处理阶段会将源代码文件中所有的预处理指令、注释信息等内容进行处理。

什么是预处理指令?

即以 # 开头,加上相应的关键字,组合成特殊的指令。

例如最常用的文件包括指令 #include。将外部的文件引入到源代码中,然后我们就可以在源代码中使用外部文件中的函数、全局变量等等。

下面聊一聊另一个用的比较多的预处理指令,宏定义指令。

1. 什么是宏定义?

宏定义又称为宏代换、宏替换,简称为宏。是 C 语言中最为常用的三种预处理指令之一。

宏定义指令,是由 # 和关键字 define 组合而成的。

它的作用是文本替换,使用标识符来代替替换列表中的内容。使用宏定义可以提高程序的通用性、易读性,减少因为输入错误产生的各种问题,同时还便于修改。

例如:

#define PI 3.1415926int r = PI * 3 * 3

此处定义了一个宏,PI。我在源代码中任意地方想要用到 3.141526 这个数值,就可以使用 PI 这个宏来替换。

当我需要圆周率后面更多位的数值,我只需要在宏定义的地方进行数值修改即可,就不需要一个个去找,去改。

常见的宏定义分为两种:无参宏定义带参宏定义

2. 无参宏定义

定义格式:

#define 标识符 替换列表

替换列表里面可以是数值常数、字符常数、字符串常数等,因此可以理解为使用标识符来表示常量,所以也被叫作符号常量。

预处理指令不是语句,因此在预处理指令的末尾是无需加上分号的。如果加上分号,可能会获得超出预期的结果。

#define N 5;
int arr[N];

虽然预处理阶段的宏定义没问题,但在编译阶段是会产生错误的。

因为宏定义只是文本替换,所以预处理的时候,会将所有的 N 替换成 5;,放到源代码中 arr[5;] 这种写法不符合 C 语言语法,所以就会产生报错。 

无参宏定义还可以替换表达式,但需要注意的是,表达式需要加上括号,否则就会出现逻辑上的错误。

例如:

#define N 3+1
int a = N * N; // 3+1*3+1=7, 不符合预期结果
// 正确方式
#define N (3+1)
int a = N * N ; // (3+1)*(3+1)=16

3. 带参宏定义

定义格式:

#define 标识符(参数列表...) 替换列表

此参数列表和函数中定义的参数列表很相似,有多个参数的时候,需要用逗号隔开。不一样的是,宏定义中的参数是不需要声明参数的数据类型。

例如:定义一个求两个值哪个大的带参宏定义。

#def MAX(a,b) ((a)>(b)?(a):(b))
int r = MAX(3, 2); // r = 2

替换列表中的每个参数以及整个替换列表都需要用括号括起来,否则就可能会出现歧义。例如:

#define MUL(a,b) (a*b)
int r = MUL(3, 3+2);

这里我们希望的结果是 r = 15,但是实际结果却是 3*3+2=11。这是因为宏定义只是文本替换,将替换列表中的内容原封不动地取代宏名。

4. 删除宏定义指令

有时候先定义了宏,后面我又重新定义了和宏名一样的变量,会发生什么情况呢?

#define N 5
int N = 3;

这里编译器会给变量 N 标出红色波浪线,给出错误提示:应输入标识符。

因为宏定义了 N 为 5,那么在编译器中有出现的 N 的地方都会被替换成数字 5。所以这里就变成 5=3,很显然 5 不符合标识符的规则,就会报错。

那么该怎么解决这个问题呢?

C 语言中也提供一种预处理指令,#undef。

它的其中一个功能就是删除前面定义过的宏,解放标识符。

#define N 5
#undef N
int N = 3;

由此可以知道,宏定义的作用域是从定义开始,直到遇到 #undef 指令,或者​程序运行完毕时才结束。​而在作用域中,就不能再使用宏定义中的标识符。

5. 宏定义和函数的区别

​学到目前,已经知道了 C 语言当中带参数的有两个,分别是宏定义和函数。

那么它们之间有什么不同之处呢?

5.1 参数类型

前面有提到一点,函数如果有参数,则必须为每一个参数指明数据类型是什么。而带参的宏定义是不需要指明参数的数据类型,因此在使用的时候可以填入任何类型的数据。

​这是宏定义的优点,同样也是缺点。没有指明数据类型,就存在​类型安全隐患。所以在设计宏定义的时候,程序员必须确保宏调用参数的类型正确。

5.2 运行时间

​宏定义是属于预处理指令,因此发生在预处理阶段,也就是在源代码编译之前。

而函数是发生在程序运行期间的。

5.3 内存空间

宏定义只是简单的文本替换,把​替换列表中的内容取代标识符的位置,替换完就会删除所有对应的标识符。因此,宏定义的参数是不需要分配空间的。

函数每次调用的时候,系统都会给形式参数重新分配一块内存空间用来存放。

5.4 执行速度

宏定义是文本替换,是不需要进行任何语法和逻辑检查,所以速度会快一些。

函数在运行阶段,参数需要经历入栈内存,然后再出栈内存的操作,所以相对的速度就会慢一些。

5.5 代码长度

如果宏定义中替换列表里面的内容是非常的长,然而预处理结束后,​替换了标识符后,代码长度就会相应的变长。

​函数就不会影响代码的长度。

一般建议,使用频繁且代码量小的功能可以使用宏定义。例如,比较两个数值的大小,或是计算某个数值的平方、三次方等等。

最后

以上就是关于 C 语言预处理指令宏定义的内容,希望对大家有所帮助。

有发现文中存在错谬之处,或者​不理解的地方,欢迎在评论区留言,一起交流学习一下。

如果您觉得本篇文章还不错,点个赞支持一下呗!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值