首先,让我们提出一个问题:当你编写单片机程序的死循环时,你更倾向于使用 for(;;)
还是 while(1)
呢?
一位工程师注意到,国外的工程师在编写演示程序时使用的是 for(;;)
,而不是通常使用的 while(1)
。这是否只是个人习惯的问题,还是背后有更深层次的含义呢?
没什么区别:只是心理作用
大多数网友认为这两者并没有本质区别,很多时候只是心理作用而已。国外工程师认为,while
循环需要在每次迭代时判断括号内的表达式是否为非零,然而经过编译器的精心优化后,while(1)
也会被优化成无条件跳转(jmp
指令),所以实际上与 for(;;)
并没有什么区别。
有人指出,for(;;)
在英语母语者那里很容易与 “forever” 这个词联系在一起。
网友们分析说,也许这只是习惯问题。实际上,在语法上 while(1)
和 for(;;)
并没有太大区别,只是 for(;;)
更明确地表示了一个无限循环,相当于使用 goto
一直跳转,没有任何比较条件。
在编译器不支持优化的前提下,while(1)
需要进行比较运算以设置寄存器 ZF
,才能执行条件跳转指令 jne
、je
。而 for(;;)
则直接使用明确的无条件转移指令 jmp
,没有任何条件跳转。
然而,实际上这并不重要,因为这些微小的差异根本无法提高代码执行的性能。现代编译器大多经过优化后,与 for(;;)
的结果几乎没有区别。
总的来说,无论你考虑什么样的优化手段,编译器都能够帮助你完成。因为编译器(尤其是开源的 GCC 和 LLVM)是由全球各地的程序员共同研发和改进的,它们的优化能力远远超过你手动修改代码所能及的。
也有网友“Shuax”使用mingw编译,实地测试一番:
for版本:
#include
int main()
{
for(;;)
{
printf("for\n");
}
}
生成汇编:
while版本:
#include
int main()
{
while(1)
{
printf("while\n");
}
}
生成汇编:
你会发现,除了文件名不同,其余都相同。
当然,这里额外说一下,不同代码、不同编译器,以及不同优化等级,可能最终结果有所差异。
正方观点:哪有好的编译器
不过,有人跳出来反驳,现代编译器的确优化很好,二者运行起来没啥区别,但是实际在嵌入式工作中,尤其是MCU编程中,可没有那么好的编译器。
一位工程师表示,很多嵌入式设备只有专用的编译器,而过去这些编译器,尤其是嵌入式编译器没做好优化的情况下,while(1)要比for(;;)多几个语句。
因为while里面是判断啊,就会变成:
label:
……
mov a, #1
jnz label
这种情况而for(;;)的话一般只会是jmp label。
许多人也有类似的经历,并表示,有些私有编译器连 (int)a
反方观点:这种代码过时了
也有工程师呼吁,不要学习这种编码风格,现在已经是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(;;)就模糊多了。不过,对于一些比较老的专用编译器来说,可能就需要慎重考虑使用哪种形式。
对现代编译器来说,二者完全就是一回事,更何况,高主频的芯片不在乎一两条机器指令了,所以这种情况下,怎么顺眼就怎么写。
以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !