嵌入式编程中使用 do{...} while(0) 的解释

最近在看esp32的idf,有一些宏定义使用了do while(0)这种看起来好像没啥用的代码。然后我查了一下资料,发现在linux内核代码中经常用到这个东西!

现在就将这个东西整理一下。

为什么在内核中碰到很多

#define ...  do{...} while(0) 

有以下几点原因:

1、空语句在编译的时候会出现警告,所以有必要用#define FOO do{ } while(0)

2、给定一个基本块,可以在里面定义局部变量

3、为了能够在条件语句中使用复杂的宏定义。例如下面这段代码:

#define FOO(x) 

     printf("arg is %s\n", x); 

     do_something_useful(x);

如果这样用:

    if (blah == 2)    F00(blah);

将会被展开为:

  if (blah == 2)

              printf("arg is %s\n", blah);

              do_something_useful(blah);

这样,if条件之后包含了printf()语句,而do_something_useful()调用不能按照预期那样工作。

而用do {...} while(0)定义后,就会展开成以下语句:

if (blah == 2)

do{

              printf("arg is %s\n", blah);

              do_something_useful(blah);

}while(0);

这是我们所期望的。

如果你希望定义一个包含多行语句和一些局部变量的时候. 一般的定义方式只能这样:

#define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }

然而在某些情况下,这样并不能正常工作. 下面是包含两个分支的if语句:

 if (x > y)

  exch(x,y);          // Branch 1

  else

  do_something();     // Branch 2

但这样却只能展开成单分支的if语句,如下:

if (x > y) {                // 单分支if

  int tmp;

  tmp = x;

  x = y;

  y = tmp;

  }

  ;                           // 空语句

  else                        // 语法错误! "parse error before else"

  do_something();

问题是由于在语句块后直接加入分号(;)引起的。

解决办法是将语句块放入 do 和 while (0)中间.这样就得到了一条单语句, 而不是被编译器判断为语句块.现在的if语句如下:

if (x > y)

  do {

      int tmp;

      tmp = x;

      x = y;

      y = tmp;

  } while(0);

 else

  do_something();

假设有这样一个宏定义

#define  macro(condition)  if(condition)  do_something();  

现在在程序中这样使用这个宏:

if(temp)
             macro(i);
else
             do_anotherthing();  

一切看起来很正常,但是仔细想想。这个宏会展开成:

if(temp)
             if(condition)  do_something();
else
             do_anotherthing();  

这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译通过了,但是运行的结果一定是错误的。  
 
为了避免这个错误,我们使用do{….}while(0)  把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。

参考资料

http://blog.csdn.net/chenhu_doc/article/details/856468

http://www.233.com/linux/fudao/20101102/140715235.html

时间: 2024-10-15 04:32:57

嵌入式编程中使用 do{...} while(0) 的解释的相关文章

0. iOS编程中启动画面的设置

这两天算是真郁闷,一个启动图片也要花一天的时间,虽然网上有介绍,也看了,可是就是不知道怎么处理,后来还是参考上一个程序的三张默认图片来设置的,320x480.png(Default.png),640x960.png([email protected]),640x1136.png([email protected]) iphone4/4s,iphone5/5s,iPad的三种尺寸.这三张图片本来是竖着放的,如果是横屏图像的话要按顺时针旋转90度来制作图片,(不知道是否还有更简便的方法,),把图片做

在嵌入式开发中应该这样理解嵌入式C编程

一.新手常常问的一个问题:C语言和嵌入式C编程有什么区别?而嵌入式工程师一般都会告诉你,其区别在于嵌入式的C语言是跑在嵌入式的开发板上的,CPU和电脑不一样,所以编译器也是不一样的,生成的可执行程序也是不一样的.不同于一般形式的软件编程,基于特定的硬件平台嵌入式系统编程的,势必要求其编程语言具备较强的硬件直接操作能力.毫无疑问,汇编语言是具备这种特点的.然而,由于该语言编写开发过程的复杂性,通常不选择它用于嵌入式系统开发,而C语言是一种"低层次"的语言,已经成为嵌入式系统开发的最佳选择

构建低成本、高度可配置的桥接解决方案:在嵌入式设计中采用基于D-PHY的MIPI标准外设

http://www.autooo.net/classid106-id128484-2.html 嵌入式系统的设计者们正面临着进退两难的困境.一方面他们需要降低系统成本.另一方面他们的系统面向使用面相对较窄.小批量的应用,无法发挥出大批量生产的规模效益.大批量的消费类应用市场提供的元件能够处理类似的任务,而且成本更低,但嵌入式系统设计者们却无法充分利用这些元件,因为他们的系统可靠性建立在为嵌入式环境优化的高度专用的那些传统接口之上.这个问题在显示屏.摄像头和应用处理器方面最为突出,适用于移动平台

【详解】嵌入式开发中固件的烧录方式

版本:v1.2 Crifan Li 摘要 本文主要介绍了嵌入式开发过程中,将固件从PC端下载到开发板中的各种方式,主要包括NFS挂载,Nand Flash和Nor Flash,USB,RS232,网卡NIC等方式. 本文提供多种格式供: 在线阅读 HTML HTMLs PDF CHM TXT RTF WEBHELP 下载(7zip压缩包) HTML HTMLs PDF CHM TXT RTF WEBHELP HTML版本的在线地址为: http://www.crifan.com/files/do

网络编程中的超时检测

我们在网络编程中常见的一种做法是:创建好套接字后以阻塞的方式读写,如果没有数据可读的话,程序会一直等待.事实上,网络状况一直不断变化,很有可能在通讯过程中出现网络连接断开.我们在程序中有必要对这种情况进行检测,从而及时做出响应.下面介绍几种常用的超时检测方法(假设我们要求通过套接字等待数据的最大时间为8秒): 一. 设置套接字接收超时 setsockopt可以设置套接字的属性,其中包括接收超时时间.参考代码如下        struct timeval tv; // 描述时间的结构体变量   

理解嵌入式开发中的一些硬件相关的概念

做嵌入式系统开发,经常要接触硬件.做嵌入式开发对数字电路和模拟电路要有一定的了解.这样才能深入的研究下去.下面我们简单的介绍嵌入式开发中的一些硬件相关的概念. 电平(Level) 在数字电路中,分为高电平和低电平,分别用1和0表示.一个数字电路的管脚,总是存在一个电平的,要么高要么低,或者说要么1要到0(其实,还有另一种状态,后面会提到). 总线(Bus) 在嵌入式系统中一定会有一块处理器芯片,此外,还有其它的芯片作为外部设备(后面简称外设),这些芯片与处理器协作实现产品的功能.复杂的产品往往是

链接脚本在编程中的高级运用之一:可变长数组

作为嵌入式软件工程师,应该要清楚程序的每一条指令在哪里,什么时候会被加载到内存,什么时候会被执行.链接脚本会明确告诉你程序的代码和数据在内存中的分布.精确控制代码和数据在内存中的分布是高效利用内存资源的前提.自定义链接脚本是资深嵌入式软件工程师的必备技能,更是嵌入式架构师的最基本要求.此外,灵活定制链接脚本在编程方面有更高级的应用. 一.编译链接原理 简单讲述编译链接的基本原理有助于后面内容的理解. a. 简单点说,一个可执行程序包括文件头.代码段(.text).数据段(.bss).符号段等信息

链接脚本在编程中的高级运用之二——执行时库和C++特性支持

我们在链接脚本在编程中的高级运用之中的一个可变长数组中已经讲述了编译链接的原理,并且以uboot命令为例具体介绍链接脚本怎样实现可变长数组. 本章在前者的基础上继续讲述链接脚本在执行时库中的高级应用技巧.以及编译器怎样支持类对象的构造和析构函数.本章的应用原则上类似于可变长数组,但本章更加側重讲述执行时库的实现原理,其不仅通过链接脚本的section来实现可变长数组去支持随意多类对象的构造函数和析构函数,并且还支持特定函数体的"可变长". 一.执行时库和类对象的构造.析构函数 非常多程

链接脚本在编程中的高级运用之二——运行时库和C++特性支持

我们在链接脚本在编程中的高级运用之一可变长数组中已经讲述了编译链接的原理,并且以uboot命令为例详细介绍链接脚本如何实现可变长数组.本章在前者的基础上继续讲述链接脚本在运行时库中的高级应用技巧,以及编译器如何支持类对象的构造和析构函数.本章的应用原则上类似于可变长数组,但本章更加侧重讲述运行时库的实现原理,其不仅通过链接脚本的section来实现可变长数组去支持任意多类对象的构造函数和析构函数,而且还支持特定函数体的"可变长". 一.运行时库和类对象的构造.析构函数 很多程序员以为程