前言
大家好,这里是 Hello_Embed。在前文中,我们学习了分支结构(if/switch),让代码能根据条件 “选择执行”;而循环结构则能让代码 “重复执行”—— 比如实现 LED 持续闪烁、反复读取传感器数据等。本文将详细讲解 C 语言中三种循环结构(while、do-while、for)的语法、差异及嵌入式场景的应用,同时介绍 break 和 continue 对循环的控制,帮助你掌握从基础语法到硬件控制的落地技巧。
🔄 while 循环:先判断后执行
语法与执行逻辑
while 循环的核心是 “先判断条件,再决定是否执行循环体”,语法如下:
while(条件表达式){
// 循环体:条件为真时执行
}
触发逻辑:
- 判断条件表达式是否为真(非 0);
- 若为真,执行循环体,执行完毕后回到步骤 1 再次判断;
- 若为假,直接跳过 while 语句,执行后续代码。
实例:计算 1-3 的和
通过简单代码理解 while 的执行过程:
#include <stdio.h>
int main()
{
int sum = 0, i = 1;
while (i <= 3) // 条件:i不大于3
{
sum = sum + i; // 累加i的值
i++; // 更新i(避免死循环)
}
printf("The sum of first 3 natural numbers is %d", sum);
return 0;
}
运行结果如图:
整个过程中,i 从 1 开始,每次循环后自增,直到 i=4 时条件不成立,循环结束。
🔄 do-while 循环:先执行后判断
语法与执行逻辑
do-while 循环与 while 的核心区别是 “先执行一次循环体,再判断条件”,语法如下:
do{
// 循环体:至少执行一次
}while(条件表达式); // 注意末尾的分号
触发逻辑:
- 先执行一次循环体;
- 判断条件表达式是否为真;
- 若为真,回到步骤 1 再次执行循环体;若为假,结束循环。
实例:同样计算 1-3 的和
对 while 的例子稍作修改,用 do-while 实现:
sum = 0;
i = 1;
do{
sum = sum + i;
i++;
} while(i <= 3); // 执行后判断
printf("\nThe sum of first 3 natural numbers is %d", sum);
运行结果如图输出仍为6
:
当循环条件一开始就成立时,while 和 do-while 的结果一致。
🆚 while 与 do while 的关键差异
通过 “计算字符串长度” 的例子对比两者的区别:
#include <stdio.h>
int main()
{
// 测试1:非空字符串
char *str = "1234567890";
int cnt = 0;
// while循环:先判断是否到结束符'\0'
while(str[cnt] != '\0') { cnt++; }
printf("The length of the string is %d\n", cnt);
cnt = 0;
// do while循环:先执行一次,再判断
do { cnt++; } while(str[cnt] != '\0');
printf("The length of the string is %d\n", cnt);
// 测试2:空字符串
char *str1 = ""; // 仅含结束符'\0'
cnt = 0;
while(str1[cnt] != '\0') { cnt++; } // 条件一开始为假,不执行
printf("The length of the string is %d\n", cnt);
cnt = 0;
do { cnt++; } while(str1[cnt] != '\0'); // 先执行一次,cnt变为1
printf("The length of the string is %d\n", cnt);
return 0;
}
注意:在这里我们定义了指针
char *str = "1234567890"
而非char类型数组是因为如下几点优势:
- 效率更高:无需复制字符串,直接引用只读内存区的原始数据(1234567890)。
- 适合只读场景:如果仅需读取字符串内容(如计算长度、遍历字符),使用指针更简洁。
- 非空字符串时,两者结果相同(均为 10);
- 空字符串时,while 返回 0(一次也不执行),do-while 返回 1(至少执行一次)。
结论:do-while 适合 “至少执行一次” 的场景(如用户输入验证),while 适合 “可能一次都不执行” 的场景。
🔄 for 循环:结构化的循环控制
语法与执行逻辑
for 循环将 “初始化、条件判断、更新” 整合在一行,结构更清晰,语法如下:
for(初始表达式; 条件表达式; 更新表达式){
// 循环体
}
执行顺序(这里引用韦东山视频教学中的框图,我觉得很形象)
- 执行一次初始表达式(如
i = 0
); - 判断条件表达式(如
i < 10
):- 若为真,执行循环体,然后执行更新表达式(如
i++
),回到步骤 2; - 若为假,结束循环。
- 若为真,执行循环体,然后执行更新表达式(如
灵活用法:死循环
for 循环的三个表达式均可省略,for(;;)
等价于while(1)
,表示条件永远为真,形成死循环:
for(;;){
// 循环体:无限重复执行
}
在嵌入式中,死循环常用于主程序逻辑,例如实现 LED 闪烁(这里仅展示部分代码):
volatile unsigned int *p = (volatile unsigned int *)0x40021414; // ODR地址
int i;
while(1) // 等价于for(;;)
{
*p &= ~(1 << 9); // 点亮LED
for(i = 0; i < 100000000; i++); // 延时
*p |= (1 << 9); // 熄灭LED
for(i = 0; i < 100000000; i++); // 延时
}
🔍 循环与分支的结合:筛选字符串中的数字
通过 “从字符串中提取数字” 的例子,看循环与 if 的结合应用:
#include <stdio.h>
int main()
{
int i = 0;
char *p = "2idiosbac901h8caioj0912"; // 含数字和字母的字符串
while (p[i] != '\0') // 循环遍历字符串,直到结束符
{
// 利用ASCII码连续性:'0'-'9'的ASCII码为48-57
if (p[i] >= '0' && p[i] <= '9') {
printf("%c", p[i]); // 打印数字字符
}
i++; // 移动到下一个字符
}
return 0;
}
运行结果如图,成功提取出所有数字。
关于代码:这里我们巧妙地利用了数字”0-9“ASCII码的连续性。我们知道0-9的ASCII码依次为:48-57,当我们要判断p[i]的ASCII码值刚好在这个范围则说明它必定为0-9,这也是我们筛选数字的基础。
🎮 循环的控制:break 与 continue
两者的区别
- break:直接跳出当前循环,不再执行后续循环;
- continue:跳过本次循环的剩余部分,直接进入下一次循环。
通过代码演示:
#include <stdio.h>
// break演示:触发后终止循环
void demo_break() {
printf("----- break演示 -----\n");
for (int i = 0; i < 3; i++) {
if (i == 1) {
printf(" 触发break,跳出循环\n");
break;
}
printf(" 循环第%d次\n", i+1);
}
}
// continue演示:触发后跳过本次
void demo_continue() {
printf("\n----- continue演示 -----\n");
for (int i = 0; i < 3; i++) {
if (i == 1) {
printf(" 触发continue,跳过本次\n");
continue;
}
printf(" 循环第%d次\n", i+1);
}
}
int main() {
demo_break();
demo_continue();
return 0;
}
运行结果如图,差异一目了然。
实例:查找字符串中的字符
用 break 优化查找效率,找到目标后立即终止循环:
#include <stdio.h>
int main()
{
int i;
int found = 0;
char *str = "fhue45icnan"; // 目标:查找'5'
for(i = 0; str[i]; i++) { // str[i]非'\0'时继续
if(str[i] == '5') {
found = 1;
break; // 找到后跳出循环
}
}
if(found == 1)
printf("Found, location is %d\n", i); // 输出位置
else
printf("Not Found\n");
return 0;
}
运行结果如图,成功定位到 ‘5’ 的位置(索引 5):
结尾
本文讲解了 while、do-while、for 三种循环的用法:while 适合简单条件判断,do-while 适合至少执行一次的场景,for 适合结构化控制;break 和 continue 则让循环更灵活。在嵌入式开发中,循环是实现周期性任务(如数据采集、状态监测)的核心,结合分支结构能构建复杂的硬件控制逻辑。
下一篇,我们将学习函数与参数的传递,看看如何将重复代码封装成函数,让程序更模块化。Hello_Embed 与你继续深入 C 语言的嵌入式应用,下篇见!