语句表达式的亮点在于定义复杂功能的宏。使用语句表达式来定义宏,不仅可以实现复杂的功能,而且还能避免宏定义带来的歧义和漏洞。下面以一个简单的最小值的宏为例子一步步说明。

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语言在宏定义中使用语句表达式和预处理器运算符相关推荐

  1. c语言解除宏定义_3.3.5 取消宏定义和重新定义宏

    3.3.5   取消宏定义和重新定义宏 #undef命令可以取消定义一个名称为宏:#undef name 这个命令使预处理器忘记name的所有宏定义.取消一个当前未定义宏的定义并不是错误.当一个名称被 ...

  2. C语言怎么判断字符YN,c语言中的宏_详解(转)

    1. 简单宏定义 简单的宏定义有如下格式: [#define指令(简单的宏)] #define 标识符替换列表 替换列表是一系列的C语言记号,包括标识符.关键字.数.字符常量.字符串字面量.运算符和标 ...

  3. c语言的44种运算符,C语言重要知识点总结【9】:C语言运算符(详解)

    目录 一.前言 二.运算符分类 三.运算符的优先级 四.常用运算符 1. 算术运算符 2. 关系运算符 3. 逻辑运算符 4. 赋值运算符 5. 条件运算符 6. 逗号运算符 7. 强制类型转换运算符 ...

  4. C语言1e12怎么识别,掌握C语言中基本的运算符

    2.3.7 逗号运算符与逗号表达式 (10) 2.4 数据类型转换 (11) 2.4.1 自动类型转换 (11) 2.4.2 赋值转换 (12) 2.4.3 强制类型转换 (12) 习题二 (13) ...

  5. c语言12之编程设计一个简单的计算器程序,要求根据用户从键盘输入的表达式:操作数1 运算符op 操作数2 计算表达式的值,指定的运算符为加减乘除。

    题目: 设计一个简单的计算器程序,要求根据用户从键盘输入的表达式: 操作数1 运算符op 操作数2 计算表达式的值,指定的运算符为加减乘除. 源代码: #include<stdio.h> ...

  6. C语言再学习 -- C 预处理器

    gcc/cc xxx.c  可以编译链接C源程序生成一个可执行文件 a.out 整个过程中可以划分为以下的4步流程: (1)预处理/预编译: 主要用于包含头文件的扩展,以及执行宏替换等 //加上 -E ...

  7. C语言入门教程||C语言 文件读写||C语言 预处理器

    C语言 文件读写 本章我们将介绍 C 程序员如何创建.打开.关闭文本文件或二进制文件. 一个文件,无论它是文本文件还是二进制文件,都是代表了一系列的字节.C 语言不仅提供了访问顶层的函数,也提供了底层 ...

  8. 【嵌入式】C语言高级编程-语句表达式(03)

    00. 目录 文章目录 00. 目录 01. C语言的表达式 02. C语言的语句 03. C语言中的代码块 04. C语言中的语句表达式 05. 宏中使用语句表达式 06. Linux内核应用示例 ...

  9. 【C语言总结】C语言预处理器

    预处理器 编译一个C程序涉及很多步骤,其中第一个步骤被称为处理阶段(preprocessing).C预处理器在源代码编译之前对其进行一些文本类性质的操作,它的主要任务包括删除注释.插入被#includ ...

最新文章

  1. 34.angularJS的{{}}和ng-bind
  2. php mysql两个表合并_php操作mysql两个数据库中表的数据同步
  3. boost::intrusive::rbtree_algorithms用法的测试程序
  4. processing pushMartix
  5. python魔术方法由谁定义_Python的魔术方法
  6. js调用vlc_如何使用HTML5或JavaScript查看RTSP流,而不使用Real Player插件上的VLC插件等插件?...
  7. iPhone 14 Pro将采用开孔全面屏:明年iPhone 15全系标配
  8. element-ui表单验证
  9. SQL中返回刚插入记录的ID
  10. CCNA路由综合实验配置详解
  11. html 360登录自动填写,汇总:如何在360浏览器中删除自动填写的表单?
  12. 数据库架构设计——索引结构设计
  13. MAE,何凯明-2021
  14. win7 thinkpad 屏幕旋转 快捷键 与 eclipse冲突
  15. 霍华德大学计算机科学,霍华德大学有哪些专业
  16. 中标麒麟linux配置网卡,中标麒麟Linux v7系统下设置双网卡bond或team绑定详细过程...
  17. android跑马灯效果横向,Android TextView 横向滚动(跑马灯效果)
  18. 大学生在线书籍网站 二手交易书籍网站制作 网页设计制作作业作品下载 dreamweaver制作静态html网页设计作业作品
  19. 项目为何要开展第三方测试
  20. 小新air15一键还原后,单击鼠标右键一直转圈

热门文章

  1. 有哪些网站社区可以看原创平面设计大师作品?
  2. 万圣节海报设计没有思路?看看这些有趣的万圣节狂欢是如何完成的!
  3. 电商设计师必备素材|快速组合自己想要的场景和落版文字
  4. UI实用素材|播放器界面模板
  5. UI实用素材|电子商务界面模板
  6. 这样的促销海报,还怕卖不出去?
  7. html文字和下划线怎么分开,HTML css样式怎样才能像下图那样把文字分开,还有下划线,请帮我补写下 ,谢谢!!...
  8. 如何低格台式计算机的硬盘,硬盘怎样低级格式化
  9. 图解Http学习第四章
  10. GTK+ tutorial