关于 while(1)和for(;;)效率问题的一点想法

本文通过实验对比了for(;;)与while(1)在嵌入式及Linux环境下生成的汇编代码,发现两者在不开优化的情况下均被编译器优化为一条跳转指令,说明它们在效率上并无明显差别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这几天看到好友的文章关于while(1)和for(;;)效率的讨论,手痒说了两句。回头一寻思,自己也只是推断。没有做任何实验,我们就看看这两种写法到底有什么区别:


实验环境:IAR EWARM 5.2

我就随便在一个嵌入式项目上做文章了,首先工程C语言编译优化选择了None, 输出选择带汇编输出,输出的汇编文件和C语言对应上。




我在main函数里先用for(;;)写了个死循环,我们看看编译结果:

注意main.c生成了一个对应的main.s




可以看到,是用一条跳转指令直接替代的for (;;)。


再更改一下代码:



编译一下看结果:


while(1)的循环也是一句跳转指令所替代。


IAR EWARM 5.2下,可见 for(;;)和while(1)在未开优化代码级别下,完全一样。无任何区别。


我们再来看看 linux 下的arm-rtems4.9-gcc的结果:



我写了一个很简单的代码:



先看看 while (1)的编译结果:


使用图形中的命令依次键入:






可以看到,while(1)循环已经变成了一个b .L7,并没有对1做判断。也是1条指令就搞定了。


我们再来看看for(;;)




键入以下命令:



查看结果如下:


两个结果是一样的。同样,gcc我也没有打开优化,如果需要看优化编译汇编结果的朋友们,可以使用gcc   -O2 -S test.c,也可以使用 gcc -Os -S test.c,-Os在嵌入式里用得多一些。没有优化的代码都是一样的,更不要说优化过的代码。


其实这是个挺蛋疼的话题,C/C++本来就书写灵活,编译器的优化是千差万别,各有特点。这让我想起了,谭浩强老师写的《C语言程序设计》,我从一开始就不认为谭老师这本书怎么样,但我也写不出更好的书来。可以肯定的是,对于书中 ++i, i++,以及执行结果和编译器高度相关的写法大量出现。害人啊。换一个编译器执行结果就不一样。实在是误人子弟。但这也是个不争的事实,那就是,规范写法,可以避免在多个编译器中移植带来的风险。


对于友人博客中所说,for(;;)和while(1)效率孰高孰低的讨论,我个人觉得:

1.本身这两种写法无任何区别,和编译器高度相关,这个是我们有能力则关心,没能力关心也不需要太关心的事;

2.嵌入式代码对C/C++写法要求很高,建议有基础的朋友们阅读阅读MISRA-C2004, 2008 和一些C++的国际级标准规范;

3.把主要的精力多放在代码的规范上,而不是代码的效率上。毕竟,单片机也使足够的快了,绝大部分情况下成立;如果你是做代码优化或者做算法的朋友们,建议多读读《计算机程序设计的艺术》一书,再掌握好一门汇编语言。将会有极大的用处。



<think>首先,用户的问题是基于引用的内容,但引用的内容是关于STM32的延时实现,包括使用循环定时器。用户明确说:“我想用户希望了解如何在STM32微控制器中使用for循环实现延时功能。” 所以,我需要专注于使用for循环实现延时的方法。 关键点: - 用户提到了“for循环”,但在引用的代码中,使用的是while循环。我需要确保回答中涉及for循环的实现。 - 引用[1]提供了STM32F103STM32F407的延时函数,使用while循环。 - 引用[2]是关于使用定时器实现延时。 用户要求使用中文回答,并遵循系统级指令: - 行内数学表达式用$...$,如$x^2$。 - 独立公式用$$...$$。 - 生成相关问题。 - 回答结构清晰,帮助用户逐步解决问题。 - 引用末尾添加引用标识。 我的回答结构: 1. **介绍**:简要解释在STM32中使用for循环实现延时的目的方法。 2. **步骤或原理**:解释如何基于系统时钟计算延时。 3. **代码实现**:提供使用for循环的延时函数代码示例,针对STM32F103STM32F407,因为引用中提到了它们。 4. **注意事项**:讨论这种方法的优缺点,如精度问题。 5. **相关问题**:生成相关问题。 确保使用for循环:在引用[1]中,使用的是while循环,但用户指定了for循环。所以,我需要修改代码为for循环。例如,将`while(us--) { i = N; while(i--); }`改为使用for循环。 数学表达式:在解释延时计算时,可能需要数学表达式。例如,延时时间计算:$ \text{延时时间} = \text{循环次数} \times \text{每条指令的时间} $。系统时钟频率为$f_{\text{sys}}$,每条指令可能需要多个周期,但通常假设为1周期/指令。 参考引用: - 引用[1]:提供了循环延时的代码。 - 引用[2]:提供了定时器实现,但用户关注循环,所以可能次要。 回答内容: - **介绍**:在STM32开发中,有时需要简单的延时功能,而不使用定时器。使用for循环是一种常见方法,通过执行空操作来消耗时间。 - **原理**:基于系统时钟频率,计算每个循环所需的时间。例如,STM32F103时钟为72MHz,假设每条指令约1个周期(但实际可能更多)。 - **代码实现**: - 对于STM32F103:系统时钟72MHz,每条指令约0.0139μs(1/72e6秒)。但实际中,需要校准。 - 提供微秒级毫秒级延时函数使用for循环。 - **修改为for循环**:原代码是while循环,我可以改写为for循环。例如: ```c void delay_us(u32 us) { for (; us > 0; us--) { for (u8 i = 0; i < N; i++) { // N是经验值 // 空操作 } } } ``` 其中N需要根据时钟频率校准。 - 根据引用[1],对于STM32F103,微秒延时中`i = 12; while(i--);`,这相当于一个内层循环。我可以写成: ```c void delay_us(u32 us) { for (; us > 0; us--) { for (u8 i = 0; i < 12; i++) { // 空操作,或使用__NOP() if available } } } ``` 但通常,空操作可以用`__asm__ volatile ("nop");`或类似,但为了简单,保持空循环。 - 同样,为STM32F407提供代码。 - **校准**:解释N值如何确定,通过实验或计算。 - **注意事项**:这种方法不精确,受优化影响,建议使用定时器或系统滴答定时器(Systick)更准确。 - **相关问题**:生成2-3个相关问题。 相关问题想法1. 如何校准for循环延时函数的参数? 2. 使用for循环延时的优缺点是什么? 3. 在STM32中,如何用Systick定时器实现精确延时? 4. 循环延时对功耗的影响? 确保回答真实可靠:基于引用一般知识。</think>### STM32使用for循环实现延时的方法 在STM32微控制器开发中,使用for循环实现延时是一种简单但粗略的方法,适用于不需要高精度延时的场景(如LED闪烁或按键消抖)。其核心原理是通过执行空循环来消耗时间,延时时间取决于系统时钟频率循环次数。下面我将逐步解释实现方法,并提供针对常见STM32型号的代码示例。 #### 1. **延时原理** 延时时间由循环次数每条指令的执行时间决定。假设系统时钟频率为$f_{\text{sys}}$(单位:Hz),每条指令约消耗1个时钟周期(实际可能因编译器优化而不同),则单次循环的时间为: $$ \Delta t = \frac{1}{f_{\text{sys}}} \times \text{指令数} $$ 例如,STM32F103的系统时钟为72MHz($f_{\text{sys}} = 72 \times 10^6$ Hz),每条指令时间约0.0139µs。要实现1µs延时,需计算内层循环次数$N$: $$ N \approx \frac{\text{目标延时}}{\Delta t} $$ 实际$N$值需通过实验校准,因为编译器优化可能改变指令执行效率[^1]。 #### 2. **代码实现(使用for循环)** 以下是针对STM32F103STM32F407的微秒级(`delay_us`)毫秒级(`delay_ms`)延时函数。代码基于引用[1]中的while循环版本修改为for循环,并添加了空操作`__NOP()`(可选,增强时间消耗)。 ##### STM32F103(系统时钟72MHz) ```c #include "stm32f10x.h" // 包含STM32头文件 // 微秒级延时函数 void delay_us(uint32_t us) { for (; us > 0; us--) { for (uint8_t i = 0; i < 12; i++) { // 经验值N=12,校准后约1µs __NOP(); // 空操作指令,可选 } } } // 毫秒级延时函数 void delay_ms(uint32_t ms) { for (; ms > 0; ms--) { for (uint16_t i = 0; i < 14500; i++) { // 经验值N=14500,校准后约1ms __NOP(); // 空操作指令 } } } ``` ##### STM32F407(系统时钟168MHz) ```c #include "stm32f4xx.h" // 包含STM32头文件 // 微秒级延时函数 void delay_us(uint32_t us) { for (; us > 0; us--) { for (uint8_t i = 0; i < 42; i++) { // 经验值N=42,校准后约1µs __NOP(); // 空操作指令 } } } // 毫秒级延时函数 void delay_ms(uint32_t ms) { for (; ms > 0; ms--) { for (uint16_t i = 0; i < 41750; i++) { // 经验值N=41750,校准后约1ms __NOP(); // 空操作指令 } } } ``` #### 3. **关键步骤说明** - **步骤1:包含头文件** 确保包含STM32型号对应的头文件(如`stm32f10x.h`或`stm32f4xx.h`),以访问`__NOP()`指令。 - **步骤2:校准循环次数** - 通过示波器或调试器测量实际延时时间。 - 调整内层循环的$N$值:$N$值越大,延时越长。 - 示例:STM32F103中,`i < 12`对应~1µs,是通过实验校准的[^1]。 - **步骤3:编译器优化处理** - 在IDE(如Keil或STM32CubeIDE)中,关闭编译器优化(设为-O0),防止循环被优化掉。 - 添加`volatile`关键字(如`volatile uint8_t i`)可强制编译器保留循环。 #### 4. **注意事项** - **优点**:实现简单,无需额外硬件(如定时器)。 - **缺点**: - 精度低(误差可达±20%),受中断系统负载影响。 - 占用CPU资源,导致功耗增加。 - 不适用于实时性要求高的场景(如通信协议)。 - **建议**:对于精确延时,优先使用Systick定时器或硬件定时器(见引用[2])[^2]。 ####
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值