【⚠️阴沟里翻船,这题都做错了!⚠️】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语言宏定义相关推荐
- 阴沟里翻船,我将11月2日定为每年的受耻日!
前几天SQL注入的问题,第二天就赶紧做了一个紧急修补,不过全面检查还没做,今天发现有人在网站上留言,说暴了我们的库(其实就暴了一个遗弃的表),并列出得到的一些数据,直指我本人,说本人技术超烂等等的话. ...
- 【转】阴沟里翻船之KeSetEvent
原帖:阴沟里翻船之KeSetEvent KeSetEvent是个使用频率很高的内核支持函数,但经常使用未必意味着确实了解它.上周就曾遇到一件怪事,系统线程在调用KeSetEvent后线程IRQL竟然从 ...
- [codeforces 1304A] Cow and Haybales 阴沟里翻船
Codeforces Round #621 (Div. 1 + Div. 2) [codeforces 1304A] Cow and Haybales 阴沟里翻船 总目录详见https://blo ...
- 阴沟里翻船之KeSetEvent
阴沟里翻船之KeSetEvent KeSetEvent是个使用频率很高的内核支持函数,但经常使用未必意味着确实了解它.上周就曾遇到一件怪事,系统线程在调用KeSetEvent后线程IRQL竟然从PAS ...
- left join 大表放前面_带娃时,走在孩子前面与跟在孩子身后区别很大,很多父母都做错了...
为人父母,都有一种天生的保护欲,特别是当孩子年幼的时候,我们恨不得永远让孩子在自己的眼皮子底下,我们担心孩子有一丁点危险. 跟年幼的孩子在外面玩耍的时候,是走在孩子前面还是走在孩子后面?目测大多数父母 ...
- JavaScript阴沟里翻船之运算符优先级
首先欢迎大家关注我的Github博客,也算是对我的一点鼓励,毕竟写东西没法变现,坚持下去也是靠的是自己的热情和大家的鼓励.各位读者的Star是激励我前进的动力,请不要吝惜. 起源 写了两年的 ...
- 阴沟里翻船——学霸的迷宫
问题描述 学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗.但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维 的格子迷宫,要进城堡必须得先通过迷宫.因为班长还有妹子要陪,磨刀 ...
- java代码编写出现的陷阱-2:阴沟里翻船
(1) 警惕自增的陷阱 java一元运算符有++,--, i++表示先赋值后加1,++i表示先加1后赋值,--同理,但是如果遇到以下代码,你恐怕是阴沟里翻船,不多说,看代码: public stati ...
- 从来不敷面膜的人_女人睡觉前,敷面膜洗还是不洗?很多人都做错了,难怪皮肤总不好...
敷面膜是众多女孩子在晚上都会进行的一个护肤工作,大家都知道像一些明星几乎是每天都要敷一片面膜的,不过她们是因为长期话大浓妆才比较勤,我们一般工作的女孩子大约一周三次就可以了. 面膜可以让我们的皮肤迅速 ...
最新文章
- 数据库中的datatime的长度怎么设定_软件测试必备之数据库知识(一)
- 企业短信平台会被其他的营销工具代替吗?企业短信平台有什么过人之处
- 对于150kHz导航信号放大检波天线保护电路
- Java接收多台设备发送消息_FCM(Firebase Cloud 消息传递)发送到多个设备
- c++ pipe 同步 互斥_数一数Linux中有多少种线程同步策略-『Linux 源码解析(二)』...
- Android学习笔记18:自定义Seekbar拖动条式样
- 【学员分享】程序员效率神器,最常用VIM插件安装大全
- java广告投放系统_[Spring cloud 一步步实现广告系统] 5. 投放系统配置+启动+实体类...
- jQuery验证表单插件——jquery-validation
- VDI中创建集合时使用Sysprep应答文件碰到的问题
- The file is absent or does not have execute permission This file is needed to run this program
- Matlab读取音频文件并进行分析
- C++多线程之_beginthread与_beginthreadex
- matlab glxs,Matlab中的textscan(转载整理)
- eas bos根据合同类别过滤自定义核算项目
- 20220721挨揍内容
- 帆软软件FineReport考试题库FCRA题库
- influence和effect的区别
- c语言 switch错误用法,C语言switch语句的详细用法
- qt中glMultiTexCoord2fARB报错
热门文章
- golang之iota
- MVC5 + EF6 入门完整教程
- 【PL/SQL】学习笔记 (7)光标的属性,一个会话中打开光标数的限制
- bzoj 3195 奇怪的道路
- 部署Django到云服务器(centos+nginx+mysql+uwsgi+python3)【操作篇(1)】
- 2017-2018-2 20179202《网络攻防技术》第一周作业
- java多线程之消费者生产者模式
- socket编程之select()
- HTML5实战 学习笔记
- (转)Silverlight显示本地图片、Stream转Byte数组