⚠️阴沟里翻船,这题都做错了!⚠️C语言宏定义

Posted 谁吃薄荷糖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了⚠️阴沟里翻船,这题都做错了!⚠️C语言宏定义相关的知识,希望对你有一定的参考价值。

前言

快答题!直接上题,10秒之内报出a的值是多少!

#include<stdio.h>
#define SQR(x) x*x
int main() {
    int a = 10, k = 2, m = 1;
    a/=SQR(k + m)/SQR(k + m);
    printf("%d\\n", a);
}

大家答完了可以到文章末尾进行投票

楔子

今天刷blink,看到一个新人在请教问题,题目代码如上,问a的值是多少。我看代码没几行,就想当然的脑算了一下,觉得结果肯定是10啊,但是本着负责的态度,还是把代码又敲了一遍,一看打印是1,我擦,翻车了。
于是乎,加打印排查。最后发现是宏的展开问题。这个函数宏少加了一个括号,展开时很容易出错。看来对宏还是理解不透彻啊!哪里跌到哪里爬起来!抓紧学起来~

宏定义

概念

C语言中的宏定义实现的是一个文本替换的功能。#define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。
命令有两种格式:

  • 简单宏定义:#define 标识符 记号序列
    示例:#define INT_MAX 2147483647
    将使得预处理器把该标识符后续出现的各个实例用给定的记号序列替换。记号序列前后的空白符将被丢弃。第二次使用#define指令定义同一标识符是错误的,除非第二次定义中的标记序列与第一次相同(所有空白分隔符被认为是相同的)。

  • 带参数的宏定义#define 标识符(标识符表opt) 记号序列
    示例:#define MAX(a,b) ((a>b)?(a):(b))
    带有形式参数(由标识符表指定)的宏定义的第一个标识符与圆括号(之间没有空格。记号序列前后的空白符也会被丢弃。如果要对宏进行重定义,则必须保证其形式参数个数、拼写及记号序列都必须与前面的定义相同。

取消宏定义

#undef 标识符用于取消标识符的预处理器定义。将#undef应用于未知标识符(即未使用#define指令定义的标识符)并不会导致错误。
示例:#undef INT_MAX

注意点

针对带参数的宏定义,宏标识符(后面可以跟一个空白符,空白符是可选的)及其后用一对圆括号括起来的、由逗号分隔的记号序列就构成了一个宏调用。宏调用的实际参数是用逗号分隔的记号序列,用引号或嵌套的括号括起来的逗号不能用于分隔实际参数。在处理过程中,实际参数不进行宏展开。宏调用时,实际参数的数目必须与定义中形式参数的数目匹配。实际参数被分离后,前导和尾部的空白符将被删除。随后,由各个实际参数产生的记号序列中的形式参数的前面一个#符号,或者其前面或后面有一个##符号,否则,在插入前要对宏调用的实际参数记号进行检查,并在必要时进行扩展。

两个特殊的运算符会影响替换过程。首先,如果替换记号序列中的某个形式参数前面直接是一个#符号(它们之间没有空白符),相应形式参数的两边将被加上双引号",随后,#和形式参数标识符将被用引号引起来的实际参数替换。实际参数中的字符串字面值、字符常量两边或内部的每个双引号""或反斜杠\\前面都要插入一个反斜杠\\

其次,无论哪种宏的定义记号序列中包含一个##运算符,在形式参数替换后都要把##及其前后的空白符都删除掉,以便将相邻记号连接起来形成一个新记号。如果这样产生的记号无效,或者结果依赖于##运算符的处理顺序,则结果没有定义。同时,##也可以不出现在替换记号序列的开头或者结尾。

对这两种类型的宏,都要重复扫描替换记号序列以查找更多的已定义标识符。但是,当某个标识符在某个标识符在某个扩展中被替换后,再次扫描并再次遇到此标识符时不再对其执行替换,而是保持不变。

即使执行宏扩展后得到的最终结果以#打头,也不认为它是预处理指令。

函数宏与函数不同点

#define ABSDIFF(a, b) ((a)>(b) ? (a)-(b) : (b)-(a))
这宏定义它返回两个参数差的绝对值。与执行同样功能的函数不同的是,参数与返回值可以是任意算术类型,甚至可以是指针。同时,参数可能有副作用,而且需要计算两次,一次进行测试,另一次则生成值。

宏的展开过程流程图

Demo

#include<stdio.h>

#define tmpfile(dir) #dir "%s"

#define cat(x, y) x ## y
#define xcat(x, y) cat(x,y)

int main() {
    printf("%s\\r\\n", tmpfile(/usr/tmp));
    printf("%d\\r\\n", xcat(xcat(1, 2), 3));
}

///< 打印信息:
///< usr/tmp%s
///< 123

引经据典

https://gaomf.cn/2017/10/06/C_Macro/
https://zhuanlan.zhihu.com/p/70366235
《The C Programming Language Second Edtion》

以上是关于⚠️阴沟里翻船,这题都做错了!⚠️C语言宏定义的主要内容,如果未能解决你的问题,请参考以下文章

❤️三万字《C语言面试错题100例》❤️(建议收藏)

C语言☀️函数超详讲解☀️(详细讲解+代码演示+图解)建议收藏

☀️光天化日学C语言☀️(35)- 局部变量和全局变量

☀️C语言函数传参の结构体数组篇☀️

☀️C语言函数传参の结构体数组篇☀️

☀️光天化日学C语言☀️(36)- 作用域