# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(二)

简介: # 工欲善其事必先利其器-C语言拓展--嵌入式C语言(二)

工欲善其事必先利其器-C语言拓展–嵌入式C语言(二)

表达式、语句和代码块–>宏

前言

提起宏,脑子里第一时间想到的就是

#define PI 3.14

但是最近在看Linux内核代码的时候,我简直了,各种花里胡哨的宏定义,我只能说有点东西。

这里的宏加入了语句表达式,一下子宏就变得精彩了。

举个栗子–>请定义一个宏,求两个数的最大值。

合格

#define MAX(x,y) x > y ? x : y

那么我们来测试一下?

MAX(1,2)
MAX(2,1)
MAX(2,2)
MAX(1!=1,1!=2)

如,测试第4行语句,当宏的参数是一个表达式,发现实际运行结果为max=0,和我们预期结果max=1不一样。

这是因为,宏展开后,变成如下样子。

printf("max=%d", 1!=1 > 1!=2 ? 1!=1: 1!=2);

因为**比较运算符>的优先级为6,大于!=(优先级为7),**所以在展开的表达式中,运算顺序发生了改变,结果就和预期不一样了。

为了避免这种展开错误,我们可以给宏的参数加一个小括号(),防止展开后表达式的运算顺序发生变化。

#define MAX(x,y) (x) > (y) ? (x) : (y)

中等

是不是觉得上面的就不错了?

那么再来个测试

3+MAX(1+2)

这按说应该等于5,但是实际上等于1。经过上面你肯定猜到了是运算符优先级变化的问题,我们展开宏的时候。

因为运算符+的优先级大于比较运算符>,所以这个表达式就变为4>2?1:2。

于是这个时候我们继续修改,用小括号将宏定义包起来。这样避免当一个表达式同时含有宏定义和其他高优先级运算符时,破坏整个表达式的运算顺序。。

#define MAX(x,y) ((x)>(y) ? (x):(y))

到这里我们加了两次括号了

良好

是不是感觉自己又行了,别高兴太早哦。

整个栗子试试

i=2;
j=6;
MAX(i++,j++)

展开

((2++)>(6++) ? (2++):(6++))

结果–>7,有没有选6的小伙伴

这是因为变量i和j在宏展开后,做了两次自增运算,导致打印出的i值为7。

在C语言编程规范中,使用宏时一般是不允许参数变化的。

但是总有些调皮鬼,要这么写应该怎么办呢?

–>语句表达式

我们可以使用语句表达式来定义这个宏,在语句表达式中定义两个临时变量,分别来暂时存储i和j的值然后使用临时变量进行比较,这样就避免了两次自增、自减问题。

看这个有点绕,看看代码

#define MAX(x, y)({ \
    int _x = x;     \
    int _y = y;     \
    _x > _y ? _x : _y;\
})

定义了2个局部变量_x、_y来存储宏参数x和y的值,然后使用_x和_y来比较大小,这样就避免了i和j带来的2次自增运算问题。

将参数与符号分开了

优秀

上面这个级别其实已经很nice了,再内核代码中我也看到了一些这样的,原来是这样的。

不过我们需要再进一步的提高自己的要求。

上面的宏只能对整形的数据进行操作,但是当不是整形难道要重新的定义吗?

那可不行哦,来来来看看下面。

#define MAX(type, x, y)({\
  type _x = x;          \
  type _y = y;          \
  _x > _y ? _x : _y;    \
})
MAX(int, i++, j++);
MAX(float, 3.14, 3.15);

在这里我们通过一个type这不就解决了,其实宏这种东西就是字符串,我觉得就是字符串的拼接替代,这样是不是会有帮助理解一点,如果没有,当我没说。

牛啊牛啊

但是我们这个type是不是感觉有点太突兀了,每次还的专门整个变量类型,如果我不知道呢?有没有想到能自动获取类型的。

这里有办法了—想起这个没–>typeof

#define max(x, y)({   \
  typeof(x) _x = (x); \
  typeof(y) _y = (y); \
  (void) (&_x == &_y);\
  _x > _y ? _x : _y ; })

有点疑惑为什么有个 (void) (&_x == &_y);\ 这个玩意?

这是用来给用户警告的,对于不同类型的指针进行比较,编译器会发出警告,提示你。

warning:comparison of distinct pointer types lacks a cast

其次这个数的比较,但是这个比较的结果用不上,可能会报warning,这个时候加一个(void)后,这个警告就pass了。(因为这个宏是发挥了作用的哦,宏在这里进行了比较呢)

内核中我看到了大量的这样的语句表达式,刚刚开始看我都问自己这还是我曾经见过的宏定义吗?

在Linux内核中,max_t和min_t的宏定义,就使用了语句表达式。

这下你知道了撒,其实就是我们常见宏的升级版本。

[学习资料]:《嵌入式C语言自我修养:从芯片、编译器到操作系统》

目录
相关文章
|
2月前
|
机器学习/深度学习 算法 数据挖掘
C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出
本文探讨了C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出。文章还介绍了C语言在知名机器学习库中的作用,以及与Python等语言结合使用的案例,展望了其未来发展的挑战与机遇。
61 1
|
2月前
|
人工智能 安全 算法
基于C语言的嵌入式系统开发,涵盖嵌入式系统概述、C语言的优势、开发流程、关键技术、应用实例及面临的挑战与未来趋势。
本文深入探讨了基于C语言的嵌入式系统开发,涵盖嵌入式系统概述、C语言的优势、开发流程、关键技术、应用实例及面临的挑战与未来趋势。C语言因其高效、可移植、灵活及成熟度高等特点,在嵌入式系统开发中占据重要地位。文章还介绍了从系统需求分析到部署维护的完整开发流程,以及中断处理、内存管理等关键技术,并展望了嵌入式系统在物联网和人工智能领域的未来发展。
96 1
|
7月前
|
C语言
C语言实现2048小游戏---粤嵌GE6818嵌入式系统实训
C语言实现2048小游戏---粤嵌GE6818嵌入式系统实训
|
7月前
|
安全 Unix Linux
嵌入式C语言(十四)
嵌入式C语言(十四)
51 0
|
5月前
|
存储 缓存 编译器
【C语言篇】scanf和printf万字超详细介绍(基本加拓展用法)(下篇)
scanf处理⽤⼾输⼊的原理是,⽤⼾的输⼊先放⼊缓存,等到按下回⻋键后,按照占位符对缓存进⾏解读。 解读⽤⼾输⼊时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条件的字符为⽌。
206 2
|
5月前
|
存储 C语言
【C语言篇】scanf和printf万字超详细介绍(基本加拓展用法)(上篇)
printf 的作⽤是将参数⽂本输出到屏幕。它名字⾥⾯的 f 代表 format (格式化),表⽰可以定制输出⽂本的格式。
102 1
|
5月前
|
算法 IDE 程序员
C语言与嵌入式系统:嵌入式C编程基础。
C语言与嵌入式系统:嵌入式C编程基础。
102 0
|
7月前
|
存储 移动开发 C语言
技术心得记录:嵌入式开发中常用到的C语言库函数
技术心得记录:嵌入式开发中常用到的C语言库函数
73 1
|
7月前
|
C语言
C语言实现电子音乐相册---粤嵌GEC6818嵌入式系统实训
C语言实现电子音乐相册---粤嵌GEC6818嵌入式系统实训
|
7月前
|
安全 Linux 编译器
嵌入式C语言(十二)
嵌入式C语言(十二)
43 1

热门文章

最新文章