c语言长度宏定义运算符,C语言在宏定义中使用语句表达式和预处理器运算符
语句表达式的亮点在于定义复杂功能的宏。使用语句表达式来定义宏,不仅可以实现复杂的功能,而且还能避免宏定义带来的歧义和漏洞。下面以一个简单的最小值的宏为例子一步步说明。
1、灰常简单的么,使用条件运算符就能完成,不就是
#define MIN(x,y) x > y ? y : x
当然这是最基本的 C 语言语法,可以写一个测试程序,验证一下我们定义的宏的正确性
#include
#define MIN(x,y) x < y ? y : x
int main(int argc, const char **argv)
{
printf("min = %d\r\n", MIN(1,2));
printf("min = %d\r\n", MIN(2,1));
printf("min = %d\r\n", MIN(2,2));
printf("min = %d\r\n", MIN(3,1+1));
printf("min = %d\r\n", MIN(1!=1, 1!=2));return 0;
}
当宏的参数是一个非简单的加减乘除的表达式时,发现实际运行结果为 min=1,和我们预想结果 min=0 不一样。这是因为,宏展开后(宏只做替换,不做运算),就变成了这个样子
printf("min = %d\r\n", 2!=2 > 1!=2 ? 1!=2 : 2!=2);
因为比较运算符 > 的优先级为6,大于 判断运算符!=(优先级为7),所以简单的宏替换的表达式展开后,运算顺序发生了改变,结果结果可想而知了。
2、通常为了避免这种展开错误,我们可以给宏的参数加一个小括号()来防止展开后,表达式的运算顺序发生变化。这样的宏才能算一个比较完善的宏:
#define MIN(x, y) (x) > (y) ? (y) : (x)
再一次进行测试,可以使用下面的代码测试:
#include
#define MIN(x, y) (x) > (y) ? (y) : (x)
int main(int argc, const char **argv)
{
printf("min = %d\r\n", 3 + MIN(1,2));
printf("min = %d\r\n", 4 + MIN(1!=2,2));return 0;
}
?
在程序中,我们打印表达式 3 + MIN(1, 2) 的值,预期结果应该是5,但实际运行结果却是2。我们展开后,发现同样有问题:
3 + (1) > (2) ? (2) : (1);
因为运算符 + 的优先级大于比较运算符 >,所以这个表达式就变为4>2?2:1,最后结果为2也就
释然了。
对于4 + (1!=2) > (2) ? (2) : (1!=2);
同样的道理,出现5>2?2:1,结果可想而知。
3、此时我们应该继续修改这个宏:
#define MIN(x, y) ((x) > (y) ? (y) : (x))
使用小括号将整个宏定义括起来,避免了当一个表达式同时含有宏定义和其它高优先级运算符时,破坏整个表达式的运算顺序。
那么现在的这个写法已经解决上面的两种写法带来的意外,在进行一下简单的测试。
#include
#define MIN(x, y) ((x) > (y) ? (y) : (x))
int main(void)
{int i = 2;int j = 6;
printf("min = %d\r\n", MIN(i++, j++));return 0;
}
?
实际运行结果发现 min = 3,而不是预期结果 min = 2。这是因为
((i++) > (j++)) ? (j++) : (i++);
在进行比较判断的时候i和j的值分别自加一次,然后在取结果的时候,i++又被执行了一次,所以,i的值变成了3。
4、基于上述的问题,可以使用语句表达式来定义这个宏。
注意:宏延续运算符(\)的概念
一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符(\)。
#define MIN(x, y) ({ int _x =x;int _y =y; _x> _y ?_y : _x;})
在语句表达式中定义两个临时变量,分别来暂储x和y的值,然后进行比较,这样就避免了两次自增、自减问题。
?
5、在上面这个宏定义的两个临时变量数据类型是 int 型。那对于其它类型的数据,就需要重新再定义一个宏,但是可以基于上面的宏继续修改,让它可以支持任意类型的数据比较大小:
#include
#define MIN(type, x, y) ({\type _x=x; type _y=y; _x> _y ?_y : _x;})int main(void)
{int i = 2;int j = 6;
printf("min = %d\r\n",MIN(int, i++, j++));
printf("min = %.2f\r\n",MIN(float, 3.14, 3.15));return 0;
}
在这个宏中,我们添加一个参数:type,用来指定临时变量 _x 和 _y 的类型。这样,我们在比较两个数的大小时,只要将2个数据的类型作为参数传给宏,就可以比较任意类型的数据了。
?
6、在内核中,尤其是在内核的宏定义中,被大量的使用。使用语句表达式定义宏,不仅可以实现复杂的功能,还可以避免宏定义带来的一些歧义和漏洞。比如在 Linux 内核中,max_t 和 min_t 的宏定义,就使用了语句表达式:
#define min_t(type, x, y) ({ \type __min1=(x); type __min2=(y); __min1< __min2 ?__min1 : __min2; })#define max_t(type, x, y) ({ \type __max1=(x); type __max2=(y); __max1> __max2 ? __max1 : __max2; })
7、宏定义中字符串常量化运算符(#)
在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)。在宏中使用的该运算符有一个特定的参数或参数列表。例如:
#include
#define PRINT(a, b) \printf(#a"and" #b "\n")int main(void)
{
PRINT(Dog abc, Cat);return 0;
}
?
8、宏定义中标记粘贴运算符(##)
宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。例如:
#include
#define tokenpaster(n) \printf ("token" #n "= %d", token##n)int main(void)
{int token12 = 34;
tokenpaster(12);return 0;
}
?
c语言长度宏定义运算符,C语言在宏定义中使用语句表达式和预处理器运算符相关推荐
- c语言解除宏定义_3.3.5 取消宏定义和重新定义宏
3.3.5 取消宏定义和重新定义宏 #undef命令可以取消定义一个名称为宏:#undef name 这个命令使预处理器忘记name的所有宏定义.取消一个当前未定义宏的定义并不是错误.当一个名称被 ...
- C语言怎么判断字符YN,c语言中的宏_详解(转)
1. 简单宏定义 简单的宏定义有如下格式: [#define指令(简单的宏)] #define 标识符替换列表 替换列表是一系列的C语言记号,包括标识符.关键字.数.字符常量.字符串字面量.运算符和标 ...
- c语言的44种运算符,C语言重要知识点总结【9】:C语言运算符(详解)
目录 一.前言 二.运算符分类 三.运算符的优先级 四.常用运算符 1. 算术运算符 2. 关系运算符 3. 逻辑运算符 4. 赋值运算符 5. 条件运算符 6. 逗号运算符 7. 强制类型转换运算符 ...
- C语言1e12怎么识别,掌握C语言中基本的运算符
2.3.7 逗号运算符与逗号表达式 (10) 2.4 数据类型转换 (11) 2.4.1 自动类型转换 (11) 2.4.2 赋值转换 (12) 2.4.3 强制类型转换 (12) 习题二 (13) ...
- c语言12之编程设计一个简单的计算器程序,要求根据用户从键盘输入的表达式:操作数1 运算符op 操作数2 计算表达式的值,指定的运算符为加减乘除。
题目: 设计一个简单的计算器程序,要求根据用户从键盘输入的表达式: 操作数1 运算符op 操作数2 计算表达式的值,指定的运算符为加减乘除. 源代码: #include<stdio.h> ...
- C语言再学习 -- C 预处理器
gcc/cc xxx.c 可以编译链接C源程序生成一个可执行文件 a.out 整个过程中可以划分为以下的4步流程: (1)预处理/预编译: 主要用于包含头文件的扩展,以及执行宏替换等 //加上 -E ...
- C语言入门教程||C语言 文件读写||C语言 预处理器
C语言 文件读写 本章我们将介绍 C 程序员如何创建.打开.关闭文本文件或二进制文件. 一个文件,无论它是文本文件还是二进制文件,都是代表了一系列的字节.C 语言不仅提供了访问顶层的函数,也提供了底层 ...
- 【嵌入式】C语言高级编程-语句表达式(03)
00. 目录 文章目录 00. 目录 01. C语言的表达式 02. C语言的语句 03. C语言中的代码块 04. C语言中的语句表达式 05. 宏中使用语句表达式 06. Linux内核应用示例 ...
- 【C语言总结】C语言预处理器
预处理器 编译一个C程序涉及很多步骤,其中第一个步骤被称为处理阶段(preprocessing).C预处理器在源代码编译之前对其进行一些文本类性质的操作,它的主要任务包括删除注释.插入被#includ ...
最新文章
- 34.angularJS的{{}}和ng-bind
- php mysql两个表合并_php操作mysql两个数据库中表的数据同步
- boost::intrusive::rbtree_algorithms用法的测试程序
- processing pushMartix
- python魔术方法由谁定义_Python的魔术方法
- js调用vlc_如何使用HTML5或JavaScript查看RTSP流,而不使用Real Player插件上的VLC插件等插件?...
- iPhone 14 Pro将采用开孔全面屏:明年iPhone 15全系标配
- element-ui表单验证
- SQL中返回刚插入记录的ID
- CCNA路由综合实验配置详解
- html 360登录自动填写,汇总:如何在360浏览器中删除自动填写的表单?
- 数据库架构设计——索引结构设计
- MAE,何凯明-2021
- win7 thinkpad 屏幕旋转 快捷键 与 eclipse冲突
- 霍华德大学计算机科学,霍华德大学有哪些专业
- 中标麒麟linux配置网卡,中标麒麟Linux v7系统下设置双网卡bond或team绑定详细过程...
- android跑马灯效果横向,Android TextView 横向滚动(跑马灯效果)
- 大学生在线书籍网站 二手交易书籍网站制作 网页设计制作作业作品下载 dreamweaver制作静态html网页设计作业作品
- 项目为何要开展第三方测试
- 小新air15一键还原后,单击鼠标右键一直转圈