do-while(0)语句到底有什么用?

news2024/12/25 8:53:54

前言

在一个群里面看到一个人问,do-while(0)语句有什么用?do-while(0)这个程序最终结果不应该就是程序只跑一次,那么写和不写有什么区别呢?

do-while(0)在复杂宏定义上的优点

为什么需要复杂宏

(1)在讲解do-while(0)在复杂宏定义上的优点前,我先介绍一下复杂宏的好处。
(2)当我们看到一个宏定义非常复杂的时候,我们很自然而然的会想到,为啥不让他变成一个函数呢?在宏定义里面搞到这么花里胡哨的,不如直接定义一个函数来的方便。
(3)当我带着这么问题去询问了交流群之后,一个大佬马上给出了回复。
<1>他说他当年一个项目8位单片机,函数都不敢深入调用,好多都是写的宏函数。因为资源有限。
<2>大佬介绍到,他哪一款单片机,硬件堆栈,好像就7层,中断预留两层,有些c标准函数用两层堆栈,自己写的就限制在3-4层左右。因为堆栈的有限,怕因为函数的深入调用如果过于频繁或递归层数过深,可能导致栈空间不足,发生堆栈溢出。当栈空间不足以容纳新的栈帧时,程序会崩溃或异常终止。
<3>所以他们当年都是这样写代码

在这里插入图片描述

优点介绍

(1)当我们在编写业务逻辑的时候,可能可能需要定义一个比较复杂的宏。如下

#define  fun printf("hello"); printf("world")
int main()
{
	fun;
	return 0;
}

(2)在这个宏的基础上,我们增加一个判断语句,就会发现问题所在。

/*******  c文件   *******/
#define  fun printf("hello"); printf("world")
int main()
{
	int a=0;
	if (a == 0)
		fun;
	else
		...
	return 0;
}

/*******  预处理之后   *******/
int main()
{
	int a=0;
	if (a == 0)
		printf("hello"); printf("world");
	else
		...
	return 0;
}

(3)通过上面的代码,我们明显的发现了。如果是在if语句中,执行这个宏,会发现第二个语句不会包含在if语句中。这样就会导致,因为 if 分支后有两个语句,导致 else 分支没有对应的 if,编译失败
(4)这个时候可能就会有人说了,那我没有else语句呢?毫无疑问,可以编译通过,但是那样的话,printf(“world”);无论如何都会执行。
(5)上述问题无疑会给程序带来异常,那么do-while(0)的用途就出来了。
<1>处理了因为if语句所带来的问题。
<2>让程序更加美观。因为在每一条语句背后,我们都习惯性的加上“;”,但是在没有加do-while(0)的宏里面,最后一条语句不能加上“;”。这样容易导致阅读障碍。

/*******  c文件   *******/
#define  fun do{                  \
				printf("hello");  \
				printf("world");  \
			   }while(0)
int main()
{
	int a=0;
	if (a == 0)
		fun;
	else
		...
	return 0;
}

/*******  预处理之后   *******/
int main()
{
	int a=0;
	if (a == 0)
		do{printf("hello");printf("world");}while(0);
	else
		...
	return 0;
}

do-while(0)对于程序释放的优缺点

优点

(1)我们在Linux内核中,常常能够看到goto语句,用于进行一些程序的收尾工作。(比如卸载驱动,销毁设备号,注销GPIO)
(2)但是因为goto不符合软件工程的结构化,而且有可能使得代码难懂,而且很容易造成bug,被一些人戏称为,C语言禁术。

/*******  使用goto  *******/
void test_func(void)
{
    //申请资源 。。。
 
    if (!condition1) {
        goto exit_entry;
    }
 
    //执行一些逻辑
 
    if (!condition2) {
        goto exit_entry;
    }
 
    //执行一些逻辑
 
    if (!condition3) {
        goto exit_entry;
    }
 
    //执行一些逻辑
 
exit_entry:
    //释放资源 。。。
 
}
/*******  使用do-while(0)  *******/
void test_func(void)
{
    //申请资源 。。。
 
    do {
        if (!condition1) {
            break;
        }
 
        //执行一些逻辑
 
        if (!condition2) {
            break;
        }
 
        //执行一些逻辑
 
        if (!condition3) {
            break;
        }
 
        //执行一些逻辑
    } while(0);
 
exit_entry:
    //释放资源 。。。
 
}

缺点

(1)既然do-while(0)比goto好,那为什么大佬们依旧喜欢使用goto呢?
(2)因为在Linux驱动开发过程中,我们需要申请很多东西,一旦其中一个东西申请失败。就需要将前面申请的都释放掉,然后返回错误。
(3)可能还是有人没有明白。我现在举个例子,一个驱动程序,需要先完成A任务,再完成B任务,最后完成C任务。然后任务结束,就先执行c任务,然后执行b任务,最后执行a任务。大写字母的任务表示创建,小写字母的任务表示释放。他们执行顺序刚好相反。

// 创建
void create()
{
	A;
	B;
	C;
}
//销毁
void destroy()
{
	a;
	b;
	c;
}

(4)以上是按照一切顺利执行来编写程序的。但是,如果我A任务,B任务或者C任务有一个执行失败了。那怎么办呢?
(5)因为我们知道,如果A任务执行失败,只需要进行a任务。而B任务执行失败,就需要进行执行b和a了。如果使用do-while(0)语句,显然就很麻烦。而使用goto就会方便很多。

// 创建
void create()
{
	A;
	if(A创建失败)
		goto A_err;
	B;
	if(B创建失败)
		goto B_err;
	C;
	if(A创建失败)
		goto C_err;
C_err:
	c;
B_err:
	b;
A_err:
	a;
}

结论

因此,我们可以得出,如果一个函数最终的出口是固定的,而且有多个if判断语句执行的情况,使用do-while(0)更好。而如果函数出口不固定,按照顺序执行,那么goto就会更优秀。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/692645.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

tinyxml2.cpp:(.text+0x71e8): undefined reference to `stdout

-target aarch64-linux-android21 > -target aarch64-linux-android24 问题解决 修改QT配置&#xff0c;默认为21改为24 &#xff0c;因为stdout stdin stderr 在android23之后才实现 编译成功&#xff0c;问题解决

C语言——详解函数栈帧的创建和销毁

函数栈帧 前言&#xff1a;一、认识相关寄存器和汇编指令1.寄存器&#xff08;寄存器是集成在cpu上的&#xff09;2.汇编指令 二、函数栈帧创建和销毁的过程1.main函数的调用2.函数栈帧的创建3.函数栈帧的销毁 前言&#xff1a; 为了深入学习C语言&#xff0c;也为了方便理解&…

仅在python虚拟环境中安装CUDA、CUDNN、PaddlePaddle-gpu

0、前言 在配置深度学习环境时&#xff0c;若想使用GPU加速&#xff0c;就需要安装CUDA、CUDNN&#xff0c;然而在系统中安装的话&#xff0c;若不同框架需要的版本不同&#xff0c;就会比较麻烦。因此&#xff0c;一种比较方便的做法是&#xff1a;利用conda管理不同的python…

arcgis实现DEM镶嵌和去白点

目录 镶嵌去白点 镶嵌 arcgis中直接选择镶嵌就行&#xff0c;然后选择其中一幅进行输出就好 去白点 镶嵌好之后&#xff0c;就会出现白色的&#xff0c;好像没有数据&#xff0c;为nodata 解决办法&#xff1a;可以使用arcgis中的焦点统计&#xff0c;计算邻域进行插值&…

terser用于ES6的压缩JS工具

https://www.npmjs.com/package/terser uglify-es不再维护&#xff0c;uglify-js也不支持ES6。 terser是uglify-es的一个分支&#xff0c;主要保留了与uglify和uglify-js3. npm install terser -gterser [input files] [options] terser-webpack-plugin 使用terser-webpack-pl…

生成式 AI 将如何颠覆数据分析?

生成式 AI 对数据分析的颠覆式变革 想象这样一个场景&#xff0c;您能够像与人交谈一样和计算机进行交流。在这个场景中&#xff0c;您不需要学习复杂的技术&#xff0c;通过自然语言就能够整理数据、分析复杂的数据集、并生成报告。几年前&#xff0c;这可能还是科幻小说中的画…

手把手教你实现SpringBoot的监控!

任何一个服务如果没有监控&#xff0c;那就是两眼一抹黑&#xff0c;无法知道当前服务的运行情况&#xff0c;也就无法对可能出现的异常状况进行很好的处理&#xff0c;所以对任意一个服务来说&#xff0c;监控都是必不可少的。 就目前而言&#xff0c;大部分微服务应用都是基…

【C#】并行编程实战:实现数据并行(1)

本教程对应学习工程&#xff1a;魔术师Dix / HandsOnParallelProgramming GitCode 到目前为止&#xff0c;我们已经掌握了并行编程、任务和任务并行的基础知识。本章将讨论并行编程的另一个重要方面&#xff0c;即数据并行。 任务并行可以为每个参与线程创建一个单独的…

SpringSecurity认证流程(超级详细)

1 .前言 最近开发项目的时候遇到了和SpringSecurity相关的一些问题&#xff0c;但是之前并没有去了解过SpringSecurity&#xff0c;导致改系统安全权限验证的时候就比较吃力了&#xff0c;目前项目开发大多都直接用脚手架直接开发&#xff0c;系统安全权限验证已经形成了&…

漏洞复现|和信创天云桌面系统存在任意文件上传目录遍历漏洞

一、 阅读须知 一切从降低已有潜在威胁出发&#xff0c;所有发布的技术文章仅供参考&#xff0c;未经授权请勿利用文章中的技术内容对任何计算机系统进行入侵操作&#xff0c;否则对他人或单位而造成的直接或间接后果和损失&#xff0c;均由使用者本人负责。 郑重声明&#x…

2019年全国硕士研究生入学统一考试管理类专业学位联考数学试题——纯题目版

2019 年 1 月份管综初数真题 一、问题求解&#xff08;本大题共 5 小题&#xff0c;每小题 3 分&#xff0c;共 45 分&#xff09;下列每题给出 5 个选项中&#xff0c;只有一个是符合要求的&#xff0c;请在答题卡上将所选择的字母涂黑。 1、某车间计划 10 天完成一项任务&a…

网络变压器/网络滤波器 国产化替代一般需要签订哪一些相关文件

Hqst华强盛导读&#xff1a;网络变压器/网络滤波器 国产化替代一般需要签订哪一些相关文件 在国内推广和应用国产替代网络变压器/滤波器时&#xff0c;需要签定一系列的文件&#xff0c;以确保网络变压器/滤波器的质量和安全&#xff0c;同时遵守国家相关的法律法规和政策规定…

JS实现选择图片剪裁及保存

JS实现选择图片剪裁及保存 以下是一个简单的示例代码&#xff0c;实现了显示一个文件上传框和一个canvas元素。用户可以选择一张图片文件后&#xff0c;该图像将显示在canvas中&#xff0c;并且用户可以通过鼠标拖拽来选取需要剪裁的区域。单击“剪裁”按钮&#xff0c;程序会…

8.11 TCP链接管理与UDP协议

目录 TCP的链接管理 TCP包头 连接的建立——”三次握手” 连接的释放——“四次挥手” 保活计时器 UDP协议 计算机网络体系结构 UDP协议 UDP的主要特点 UDP是面向报文的 TCP的链接管理 TCP包头 连接的建立——”三次握手” TCP 建立连接的过程叫做握手。 采用三报文…

java进阶—重要概念反射

反射概念 反射: 它是java中的一个很重要的概念,是框架设计的灵魂 框架呢&#xff1f;就是一个半成品软件&#xff0c;我们在这半成品上进行开发&#xff0c;比如我们经常提到spring springmvc springboot spingcloud 等等 也许有的小伙伴会说&#xff0c;框架别人都写好了&a…

Tdengine 时序数据库-安装与客户端连接

使用 TDengine 时序数据库的版本是 2.4.0.0 使用的安装RPM的安装方便安装 TDengine-server-2.4.0.0-Linux-x64.rpm 1. 安装指令: rpm -ivh TDengine-server-2.4.0.0-Linux-x64.rpm [rootnode3 server]# rpm -ivh TDengine-server-2.4.0.0-Linux-x64.rpm Verifying... …

python的格式化输出

print中的占位符(%-fomatting) print中的%d,%s等&#xff0c;只是一个占位符&#xff0c;等正确内容到了再查进去。 注意&#xff1a;变量值和打印内容之间不能有逗号隔开。 使用fomat方法 format底层可以理解成一个字典&#xff0c;默认key是按顺序从0开始&#xff0c;指定key…

nginxWebUI runCmd RCE漏洞复现

0x01 产品简介 nginxWebUI是一款图形化管理nginx配置的工具&#xff0c;能通过网页快速配置nginx的各种功能&#xff0c;包括HTTP和TCP协议转发、反向代理、负载均衡、静态HTML服务器以及SSL证书的自动申请、续签和配置&#xff0c;配置完成后可以一键生成nginx.conf文件&#…

网络变压器卷盘(封装载带)封膜压力标准

Hqst华强盛导读&#xff1a;网络变压器封装载带封膜压力标准通常是根据具体产品的封装材料和尺寸来确定的。 网络变压器封装载带封膜压力标准通常是根据具体产品的封装材料和尺寸来确定的。一般来说&#xff0c;封装载带的封膜压力应该足够大&#xff0c;以确保封装过程中封膜…

安卓开发基于KeyStore对数据进行加解密

问题背景 在我们App开发过程中&#xff0c;可能会涉及到一些敏感和安全数据需要加密的情况&#xff0c;比如登录token的存储。我们往往会使用一些加密算法将这些敏感数据加密之后再保存起来&#xff0c;需要取出来的时候再进行解密。 此时就会有一个问题&#xff1a;用于加解密…