丈夫志四海,万里犹比邻。这篇文章主要讲述C语言的预编译(预处理)#define相关的知识,希望能为你提供帮助。
@TOC
一、预定义符号预定义符号是系统本身定义的:
- FILE进行编译的源文件的位置
- LINE文件当前的行号
- DATE文件被编译的日期
- TIME文件被编译的时间
- STDC如果编译器遵循 ASNSI C,其值为1,否者未定义
文章图片
二、#define 定义标识符语法:#definenamestuff(用stuff替换name)
#define MAX 100
#define STR "hehe"
int main()int max = MAX;
printf("%d\\n", max);
//输出100
printf("%s\\n",STR);
//输出 hehe
return 0;
三、#define 定义宏
- #define 机制包括了一个机制,允许把参数替换到文本中,这种实现通常称为宏或者宏定义
- 宏的申明方式:#define name(parament-list)stuff其中的parament-list是一个由逗号隔开的符号表,他们可能出现在stuff中。
- 注意:参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会解释为stuff 的一部分。
#define SQUARE(X) X*X int main()int ret = SQUARE(5); printf("%d\\n",ret); //输出25 return 0;
上面的宏定义代码存在一定的问题: 如果我们换一个参数(将5换成5+1)输出的不是36而是11为什呢?
#define SQUARE(X) X*X int main()int ret = SQUARE(5+1); //替换之后就是(5+1*5+1 = 11) printf("%d\\n",ret); //输出11 return 0;
没加括号
因此,用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或临近操作符之间不可预料的相互作用。
#define SQUARE(X) (X)*(X))
int main()int ret = SQUARE(5+1);
printf("%d\\n",ret);
//输出36
return 0;
四、#define 替换规则:
- 一, #define NAME“lisa”
程序中有" NAME" ,但”“内的东西不会被宏替换。 - 二,宏定义前面的那个必须是合法的用户标识符
- 三,宏定义也不是说后面东西随便写,不能把字符串的两个”“拆开。
- 四: #define NAME “lisa”
程序中有上面的宏定义,并且,程序里有句:
NAMELIST这样,不会被替换成" lisa" LIST - 五,宏不能出现递归
如果我们想要实现一个代码:把参数插入到字符串中 用到“#”
文章图片
这里参数a,b就插入到了字符串中了
##的作用:可以把位于它两边的符号合成一个符号,它允许宏定义冲从分离的文本片段创建标识符。
文章图片
图中的三句代码是等价的
printf(" %d\\n" ,AGE(lisa,24));
printf(" %d\\n" ,AGE(lisa##24));
printf(" %d\\n" ,AGE(lisa24));
六、宏和函数的对比函数和宏都能实现求两个数的最大值
//函数
int Max(int x, int y)return (x >
y ? x : y);
//宏
#define MAX(X,Y) ((X)>
(Y)?(X):(Y))
int main()int a = 10;
int b = 20;
int max = Max(a, b);
//输出20
printf("%d\\n",max);
max = MAX(a, b);
printf("%d\\n", max);
//输出20
return 0;
通过分析上面的代码实现用宏比用函数会更好,有两个原因:
- 用于调用函数和从函数返回的代码可能比实际执行的这个小型计算工作所需要的时间更多,所以宏比函数在程序的规模和速度方面更胜一筹。
- 函数的参数必须申明为特定的类型。所以函数只能在类型合适的表达时式上使用。反之宏是与类型无关的。
- 每次使用宏的时候,一份宏定义的代码将替换插入到程序中。除非宏比较短,否者可能大幅度增加程序的长度。
- 宏没法调试
- 宏由于类型无关,也就不够严谨
- 宏可能会带来运算符优先级的问题,导致程序容易出错。
#define定义宏和函数的对比表格 |
属性 | #define定义宏 | 函数 |
---|---|---|---|
代码长度 | 每次使用时宏代码都会被插入到程序中除了非常小的宏之外,程序的长度会大幅度增长 | 函数的代码只出现在一个地方,每次使用这个函数时,都调用那个地方的同一份代码 | |
执行速度 | 更快 | 存在函数的调用和函数的额外开销,所以速度相对慢一些 | |
操作符优先级 | 宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否者邻近操作符的优先级可能产生不可预料的结果,所以建议宏在书写的时候多用括号 | 函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。 | |
带有副作用的参数 | 参数可能被替换带宏中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果 | 函数参数只在传参的时候求值一次,结果更容易控制 | |
参数类型 | 宏的参数与类型无关,只要对参数的操作是合法的,它就可以适用于任何参数类型 | 函数的参数与类型有关,如果参数的类型不同,就需要不同的函数,即使他们执行任务是不同的 | |
调试和递归 | 宏不方便调试,不能递归 | 函数可以逐语句调试,可以递归 |
七、#undef【C语言的预编译(预处理)#define】#undef 指令用于移除一个宏定义
当#undef 移除宏定义,再次使用报错。如图 :
文章图片
推荐阅读
- FreeRTOS快速入门-初探FreeRTOS
- 类和对象—5
- #yyds干货盘点# 解决剑指offer(机器人的运动范围)
- Kafka生成消息时的3种分区策略
- kettle庖丁解牛第13篇之XML文件输入
- 系统之家精简纯净win7系统让网络文件夹也入“库”的窍门
- 妙用FinalRecovery软件对系统之家hpwin7纯净版系统IDE硬盘健康诊断
- 设置系统之家纯净win7系统系统任务栏窗口不合并的技巧
- 系统之家纯净win7系统桌面添加4角技巧键的窍门