[疑难问答] 使用for(;;)来实现MCU死循环

[复制链接]
3015|38
maqianqu 发表于 2025-8-23 22:40 | 显示全部楼层 |阅读模式
首先,问大家一个问题:你们写单片机程序【死循环】时,喜欢用用 for(;;) 还是 while(1)?

一位工程师发现,国外工程师在给demo在做死循环时用的是for(;;),而不是常用的while(1)。这仅仅是个人习惯的问题,还是有更深层次的含义?

没啥区别党:都是心理作用

大部分网友认为二者并没有什么区别,很多时候,只是心理作用,国外工程师认为while需要经过判断括号里的表达式是否非0才跳转。但经过编译器的精心优化以后,while(1)也会被优化成无条件跳转(jmp指令),所以跟for(;;)没什么区别。   有人表示,for(;;)在英语母语者那里很容易跟forever挂钩。   网友解析,也很有可能是习惯问题,其实while(1),还是for(;;)两个语法上有啥区别,那就是for(;;) 明确就是循环,等价于goto一直跳,没有比较条件。   while不编译支持优化的前提下都需要做cmp运算设置寄存器ZF,才能jne,je指令条件跳转。而for(;;)就是明确的jmp无条件转移eip,没有jne,je条件跳转。   不过其实无所谓的,这根本不能提高任何一点代码执行的性能。因为现代编译器大多优化以后跟for(;;)的结果没得什么区别。   其实你所考虑到的一切优化手段,编译器都能帮你完成,因为编译器(尤其是开源的GCC和LLVM)是由来自全球各地的程序员共同研发并改进的,它们的优化能力远远强于你手动改进代码。   也有网友“Shuax”使用mingw编译,实地测试一番:   for版本:


#includeint main(){  for(;;)  {    printf("for");  }}   生成汇编:

  while版本:
#includeint main(){  while(1)  {    printf("while");  }}  生成汇编:

  你会发现,除了文件名不同,其余都相同。   当然,这里额外说一下,不同代码、不同编译器,以及不同优化等级,可能最终结果有所差异。  

正方观点:哪有好的编译器

不过,有人跳出来反驳,现代编译器的确优化很好,二者运行起来没啥区别,但是实际在嵌入式工作中,尤其是MCU编程中,可没有那么好的编译器。   一位工程师表示,很多嵌入式设备只有专用的编译器,而过去这些编译器,尤其是嵌入式编译器没做好优化的情况下,while(1)要比for(;;)多几个语句。   因为while里面是判断啊,就会变成:  


label:    ……    mov a, #1    jnz label  这种情况而for(;;)的话一般只会是jmp label。   许多人也有类似的经历,并表示,有些私有编译器连 (int)a<<0 这种都能生成非法指令,不由地怀疑配套的破芯片到底能不能受得了各种优化过的指令。  

反方观点:这种代码过时了

也有工程师呼吁,不要学习这种编码风格,现在已经是2024年了,用for(;;)表示无限循环已是一种过时的风格了。   从施特劳斯特撸普博士到我国国家军用标准,均认为 for(;;) 是一种不良风格,可参见:  

GJB 8114-2013 R-1-9-4:无限循环必须使用while(1)语句,禁止使用for(;;)等其他形式

CppCoreGuidelines ES.73:Prefer a while-statement to a for-statement when there is no obvious loop variable

360 safe rules: for语句没有明确的循环变量时应改用while句语

这是为什么呢?在较为严格的规范体系内,for 语句专用于实现具有明确循环次数和循环变量的迭代算法,小括号内的三个表达式应分别专注于循环变量的初始化、循环条件的判断、循环变量的增减,这样可以使循环具有清晰的静态结构,便于阅读,利于维护。如果没有明确的循环变量,则应改用 while 循环,避免对代码的维护者造成误导。   有人说for(;;)表示无条件循环,while(1)需要作条件判断,效率比for(;;)慢,有一定道理,但那都是很早以前的事情了,现在即使没有编译器优化,这种开销也不会成为效率的瓶颈,是不值得优化的,保持代码清晰的静态结构更为重要!   类似于国军标这种严格的代码审计规则,可参见: github.com/Qihoo360/safe-rules  

工程师实地测试:和编译器和优化有关

公众号博主“WKJay”也在STM32F103、ARMCC5进行过测试,将两个逻辑分别运行一下(不开编译器优化),查看逻辑分析仪输出的结果。   while(1) 逻辑运行结果:  

  for(;;) 逻辑运行结果:  

  结果显示,虽然循环体完全相同,但实际运行结果来看,for(;;) 语句执行得更快(45.863ms),比 while(1)(48.643ms) 快了5.7%左右。   根据他的分析,for的指令更精简,而while的指令相对更繁琐,简而言之,for抄了近道,而while弯弯绕绕。  

  最后,他开启了编译器的O3优化,结果,二者就几乎不存在差别了(12.505ms):  

  从可读性角度来说,while(1)简单清晰,for(;;)就模糊多了。不过,对于一些比较老的专用编译器来说,可能就需要慎重考虑使用哪种形式。   对现代编译器来说,二者完全就是一回事,更何况,高主频的芯片不在乎一两条机器指令了,所以这种情况下,怎么顺眼就怎么写。  


tabmone 发表于 2025-9-8 10:17 | 显示全部楼层
for(;;) 属于三个表达式全省略的特例:

无初始化操作;
无循环条件(默认条件为 “真”);
无增量操作。
houjiakai 发表于 2025-9-8 13:05 | 显示全部楼层
编译器对两种循环的优化策略不同,可能导致生成的机器码略有差异
bestwell 发表于 2025-9-8 19:39 | 显示全部楼层
更容易被编译器识别为无条件的死循环,直接生成跳转指令
wilhelmina2 发表于 2025-9-9 12:07 | 显示全部楼层
do{...}while(true) 的特点是先执行一次循环体再判断条件,即使初始条件不满足也会至少执行一次
chenci2013 发表于 2025-9-9 18:07 | 显示全部楼层
与for(;;)一致,无条件跳转指令实现循环。
louliana 发表于 2025-9-10 10:45 | 显示全部楼层
使用 for(;;) 实现 MCU 死循环是一种常见的编程模式
gygp 发表于 2025-9-10 12:47 | 显示全部楼层
while(1)更直观,易于快速理解。
ccook11 发表于 2025-9-10 15:09 | 显示全部楼层
推荐for(;;),强调“无退出条件”的纯粹循环。
wangdezhi 发表于 2025-9-10 17:37 | 显示全部楼层
推荐while(1)              
51xlf 发表于 2025-9-10 19:39 | 显示全部楼层
语法简洁、性能最优且意图明确              
alvpeg 发表于 2025-9-10 21:55 | 显示全部楼层
省略所有表达式(初始化、条件、迭代),循环条件恒为真。
beacherblack 发表于 2025-9-13 13:18 | 显示全部楼层
初始化、条件判断、迭代三部分均为空,形成无限循环。
loutin 发表于 2025-9-13 15:43 | 显示全部楼层
for(;;) 会无条件重复执行循环体,形成死循环,这与 while(1) 的逻辑完全一致
beacherblack 发表于 2025-9-13 16:07 | 显示全部楼层
for(;;) 通常会被优化为简单的跳转指令
rosemoore 发表于 2025-9-13 16:28 | 显示全部楼层
需要初始化或迭代操作的死循环              
nomomy 发表于 2025-9-13 18:00 | 显示全部楼层
C 语言中 for 循环的语法是:
for(初始化表达式; 循环条件; 增量表达式)
wilhelmina2 发表于 2025-9-13 18:28 | 显示全部楼层
需绝对高效时,for(;;) 避免冗余指令
hearstnorman323 发表于 2025-9-13 19:09 | 显示全部楼层
while(true) 依赖显式的布尔常量作为条件,虽然效果相同,但需每次循环都进行一次冗余的条件判断
hudi008 发表于 2025-9-13 19:28 | 显示全部楼层
for(;;)和while(1)的调用栈显示无区别,均表现为无限循环。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

2752

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部