C语言入门系列:流程控制
一,C代码执行顺序
默认情况下,C语言程序从main()函数开始执行,随后按源代码中语句出现的顺序逐一执行。
这意味着,如果不考虑任何控制结构,程序会自上而下,逐行执行每条语句,直到遇到函数调用、返回语句或程序结束。
但是,在实际编程中,很多任务和逻辑无法仅通过简单的从上到下的顺序执行来完成。以下通过几个典型场景来说明这一点:
1. 条件执行:决定性操作
假设你正在编写一个程序来判断一个数是正数、负数还是零。
如果不使用控制语句,程序将无法根据输入的数值做出不同的反应,因为所有的操作都将按顺序执行,无法选择性地执行特定代码。
使用if语句,可以轻松实现这一点:
#include <stdio.h>int main() {int num = 5;if (num > 0) {printf("正数\n");} else if (num < 0) {printf("负数\n");} else {printf("零\n");}return 0;
}
2. 循环处理:重复性任务
如果你需要打印出1到10的数字,仅仅依靠顺序执行是不够的,因为你需要让相同的打印操作重复执行10次。
对于这个星期,调用10次printf即可。但如果是重复1万次呢,在代码中调用1万次printf,那也太傻了吧?
这时,循环控制语句(如for循环)就显得尤为重要:
#include <stdio.h>int main() {for(int i = 1; i <= 10; i++) {printf("%d ", i);}printf("\n");return 0;
}
3. 跳出循环:提前结束或跳过
在某些情况下,可能需要在满足特定条件时提前结束循环,或者在循环内部的某些点跳过某些步骤。这时,break和continue控制语句就发挥了作用:
#include <stdio.h>int main() {for(int i = 1; i <= 10; i++) {if(i == 5) {printf("遇到5,提前结束循环\n");break; // 跳出循环}if(i % 2 == 0) {continue; // 跳过偶数,不打印}printf("%d ", i);}printf("\n");return 0;
}
二,控制类型
C语言中的流程控制语句可以大致分为以下几类:
- 条件分支语句
- 循环语句
- 跳转语句
- 其他控制
1. 条件分支语句
- if 语句:基于一个条件,决定是否执行某段代码。
- 三元运算符(条件运算符)
?::一种简洁的条件表达式形式,用于赋值操作,根据条件选择两个表达式之一。 - switch 语句:用于多路分支选择,根据不同的表达式值执行不同的代码块。
2. 循环语句
- while 语句:只要给定条件为真,就重复执行一段代码。
- do…while 结构:先执行循环体,然后检查条件是否为真,如果是,则继续执行循环。
- for 语句:一种复合循环结构,集成了初始化、条件检查和迭代更新于一身。
3. 跳转语句
- break 语句:用于提前退出循环或switch语句。
- continue 语句:跳过当前循环迭代的剩余部分,直接开始下一次迭代。
- goto 语句:允许程序跳转到同一函数内的指定标签处,虽然可用,但因其可能引起代码混乱和可读性差,现代编程实践中通常不鼓励使用。
4. 其他控制结构
- 虽然不是C语言标准库直接提供的流程控制语句,但通过组合基本的流程控制语句,可以实现更复杂的控制结构,如:
- 循环嵌套:一个循环内部包含另一个循环。
- 条件循环:在循环条件中使用逻辑表达式来控制循环的执行。
- 提前返回(return):在函数中使用,可以立即结束函数的执行并返回一个值(对于非void类型函数)。
三,条件分支语句
条件分支语句让程序能够根据条件跳过某些代码、执行特定代码。
1. if 语句
基本语法
if语句用于条件判断,满足条件时,就执行指定的语句,不满足就跳过对应的语句。
if (expression) statement
上面式子中,表达式expression为真(值不为0)时,就执行statement语句,statment部分称之为代码块。
if后面的判断条件expression外面必须有圆括号,否则会报错。代码块可以是一条语句或多条语句,多条语句必须包含在一对大括号内。
下面是一个一条语句例子。
if (x == 10) printf("x is 10");
上面示例中,当变量x为10时,就会输出一行文字。
为了更好的可读性,语句部分通常另起一行。
if (x == 10)printf("x is 10\n");
如果代码块包含多条语句,就需要把它们放在大括号里面,组成一个复合语句。
if (line_num == MAX_LINES) {line_num = 0;page_num++;
}
我们建议,无论是一行语句,还是多行语句,都用大括号包裹起来,有更好的可读性,避免出现意外的错误。
if (x == 10) {printf("x is 10\n");
}
if-else
if语句可以带有else分支,指定条件不成立时(表达式expression的值为0),所要执行的代码。
if (expression) statement
else statement
下面是一个例子。
if (i > j)max = i;
elsemax = j;
如果else的语句部分多于一行,同样必须把它们放在大括号里面。
多重判断
else可以与另一个if语句连用,构成多重判断。
if (expression)statement
else if (expression)statement
...
else if (expression)statement
elsestatement
如果有多个if和else,可以记住这样一条规则,else总是跟最接近的if匹配。
if (number > 6)if (number < 12)printf("The number is more than 6, less than 12.\n");
elseprintf("It is wrong number.\n");
上面示例中,else部分匹配最近的if(即number < 12),所以如果number等于6,就不会执行else的部分。
不过,为了提供代码的可读性,建议使用大括号,明确else匹配哪一个if。
if (number > 6) {if (number < 12) {printf("The number is more than 6, less than 12.\n");}
} else {printf("It is wrong number.\n");
}

强烈建议,if和else后的代码块不要省略大括号,哪怕在只有一行语句的情况下。
2. 三元运算符 ?:
三元运算符是if...else的简写形式,用于简单的条件赋值。其语法结构如下。
<expression1> ? <expression2> : <expression3>
下面是一个示例。
#include <stdio.h>int main() {int score = 75;char result = (score >= 60) ? 'P' : 'F';printf("Your grade is: %c\n", result);return 0;
}
这段代码可以用if-else改写。
#include <stdio.h>int main() {int score = 75;char result;if (score >= 60) {result = 'P';} else {result = 'F';}printf("Your grade is: %c\n", result);return 0;
}
3. switch 语句
switch语句根据不同的情况执行不同代码块,每个情况由一个或多个case标签指定。
switch 语句可以理解为一种特殊形式的 if-else 结构,switch 语句更易用、可读性更好。
switch (expression) {case value1: statement1case value2: statement2default: statement3
}
这个结构表达的是,expression 的值匹配到某个case后,执行对应的statement。
举例如下。
#include <stdio.h>int main() {int day = 3;switch(day) {case 1:printf("Monday\n");break;case 2:printf("Tuesday\n");break;case 3:printf("Wednesday\n");break;default:printf("Invalid day\n");}return 0;
}
上面switch语句表达的是,根据整数类型变量day的值选择执行特定的代码。若day的值是1,输出Monday,如果是2,输出Tuesday,如果是3,输出Wednesday,如果都不是,执行default对应的语句,输出Invalid day。因为上述代码中变量day的值是3,所以会执行printf("Wednesday\n");,输出Wednesday。
注意,每个case语句体的结尾,都应该有一个break语句,作用是跳出整个switch结构,不再往下执行。如果缺少break,就会继续执行下一个case或default分支。
上述代码在执行代码printf("Wednesday\n");后,遇到break语句,跳出switch语句,执行其后的语句return 0;。
如下代码,第2个case的代码块没有break语句。
#include <stdio.h>int main() {int day = 2;switch(day) {case 1:printf("Monday\n");break;case 2:printf("Tuesday\n");case 3:printf("Wednesday\n");break;default:printf("Invalid day\n");}return 0;
}
因为day的值是2,所以执行 printf("Tuesday\n");,但由于没有break语句,会接着执行第三个case的代码块,即执行代码printf("Wednesday\n");,然后因为break语句,跳出switch语句。
本意是当day值为3时,才输出Wednesday,但因为第2个case后的代码块缺少break,导致错误发生。

如果要省略case后代码块的break,一定要明确不会出现意外的情况。
四,循环语句
1. while 语句
while语句用于循环结构,满足条件时,不断执行循环体。
while (expression)statement
statement部分称之为循环体。
上面代码中,如果表达式expression为非零值(表示真),就会执行statement语句,然后再次判断expression是否为零;如果expression为零(表示伪)就跳出循环,不再执行循环体。
下面代码的作用是判断一个整数有多少位,用while循环实现。
#include <stdio.h>int main() {int count = 0;int num = 2323233232;int tmpNum = num;while(tmpNum > 0) {count++;tmpNum= tmpNum / 10;}printf("num=%d是%d位数", num, count);return 0;
}
上述代码不够健壮,没有考虑临界情况,即当num = 0时,程序不能得出正确结果。
在使用循环体时,要特别注意临界问题。
我们做如下优化:
#include <stdio.h>int main() {int count = 0;int num = 2323233232;int tmpNum = num;// 临界情况,特殊处理if (num == 0) {count++;}while(tmpNum > 0) {count++;tmpNum= tmpNum / 10;}printf("num=%d是%d位数", num, count);return 0;
}
注意,当循环体中只有一条语句时,可以省略大括号。有多条语句时,不能省略。但强烈建议,即便只有一条语句,也不要省略大括号。
#include <stdio.h>int
main ()
{int day = 2;// 省略大括号,语法正确while (day < 3)printf ("day=%d\n", day++);printf ("day=%d", day++);return 0;
}
加上大括号,可读性更强。
#include <stdio.h>int
main ()
{int day = 2;// 省略大括号,语法正确while (day < 3) {printf ("day=%d\n", day++);}printf ("day=%d", day++);return 0;
}
2. do…while 结构
与while类似,最大的区别是do-while先执行一次循环体,然后检查条件,所以其至少会执行一次循环体。
用do-while重写计算整数位数的例子。
#include <stdio.h>int main() {int count = 0;int num = 2323233232;int tmpNum = num;do {count++;tmpNum= tmpNum / 10;} while(tmpNum > 0)printf("num=%d是%d位数", num, count);return 0;
}
如果用do-while,临界情况num = 0就无需特殊处理。
同样,我们建议循环体始终包裹在大括号中。
3. for 语句
for语句是最常用的循环结构,通常用于精确控制循环次数。
for (initialization; continuation; action)statement;
上面代码中,for语句的条件部分(即圆括号里面的部分)有三个表达式。
- initialization:初始化表达式,用于初始化循环变量,只执行一次。
- continuation:判断表达式,只要为true,就会不断执行循环体。
- action:循环变量处理表达式,每轮循环结束后执行,使得循环变量发生变化。
#include <stdio.h>int main() {for(int i = 0; i < 5; i++) {printf("%d ", i);}printf("\n");return 0;
}
五,跳转语句
1. break 语句
break用于立即退出最内层循环或switch语句。
break语句有两种用法。
- 一种是与switch语句配套使用,用来中断某个分支的执行,这种用法前面已经介绍过了。
- 另一种用法是在循环体内部跳出循环,不再进行后面的循环了。
for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {printf("%d, %d\n", i, j);break;}
}
上面示例中,break语句使得循环跳到下一个i。
注意,break命令只能跳出循环体和switch结构,不能跳出if结构。
if (n > 1) {if (n > 2) break; // 无效printf("hello\n");
}
上面示例中,break语句是无效的,因为它不能跳出外层的if结构,还会继续执行printf("hello\n")。
注意事项
- 在多层循环中,
break只跳出当前层循环。
2. continue 语句
continue使程序跳过当前循环体剩余部分,直接开始下一次循环迭代。
#include <stdio.h>int main() {for(int i = 0; i < 10; i++) {if(i % 2 == 0) { // Skip even numberscontinue;}printf("%d ", i);}printf("\n");return 0;
}
上面示例中,只要是偶数,就用continue语句跳过该数字,执行下一次循环
3. goto 语句
goto允许无条件跳转到标记的语句,但在现代编程中不鼓励使用,因为它可能导致代码结构混乱,不易理解,所以这里一笔带过。
#include <stdio.h>int main() {int i = 0;
start:printf("%d ", i);i++;if(i < 5) goto start; // 不推荐使用printf("\n");return 0;
}
尽量避免使用goto,考虑使用循环或函数替代以提高代码质量。
六,其他控制结构
虽然不是C语言标准库直接提供的流程控制语句,但通过组合基本的流程控制语句,可以实现更复杂的控制结构,如:
- 循环嵌套:一个循环内部包含另一个循环。
- 条件循环:在循环条件中使用逻辑表达式来控制循环的执行。
- 提前返回(return):在函数中使用,可以立即结束函数的执行并返回一个值(对于非void类型函数)。
相关文章:
C语言入门系列:流程控制
一,C代码执行顺序 默认情况下,C语言程序从main()函数开始执行,随后按源代码中语句出现的顺序逐一执行。 这意味着,如果不考虑任何控制结构,程序会自上而下,逐行执行每条语句,直到遇到函数调用…...
【深度学习】NLP,Transformer讲解,代码实战
文章目录 1. 前言2. Transformer结构训练过程1. 输入嵌入和位置编码2. 编码器层2.1 单头的注意力机制(便于理解)2.2 多头的注意力机制(Transformer真实使用的)2.3 残差连接和层归一化2.4 前馈神经网络(FFN)2.5 残差连接和层归一化2.6 总结 3. 解码器层 推…...
ES6 .entries用法
.entries() 返回数组的可迭代对象itarator,可以通过for of 来遍历数组或对象的值。但是它们的写法不一样。 数组用法 obj [apple,bananer,orange] for(let [i,v] of obj.entries()){console.log(i)console.log(v) } //输出 // 0 // apple // 1 // bananer // 2 //orange对象…...
【LeetCode 动态规划】买卖股票的最佳时机问题合集
文章目录 1. 买卖股票的最佳时机含冷冻期 1. 买卖股票的最佳时机含冷冻期 题目链接🔗 🍎题目思路: 🍎题目代码: class Solution { public:int maxProfit(vector<int>& prices) {int n prices.size();ve…...
python学习:语法(2)
目录 对象的布尔值 分支结构 双分支结构 多分支结构 嵌套if的使用 条件表达式 Pass语句 range()函数的使用 流程控制语句 对象的布尔值 Python一切皆对象,所有对象都有一个布尔值,通过内置函数bool()获取对象的布尔值 这些…...
经典电源电路基础(变压-整流-滤波-稳压)
1.电源电路的功能和组成 电子电路中的电源一般是低压直流电,先把220v交流电变换成低压直流电,再用整流电路变成脉动的直流电,最后用滤波电路滤除掉脉动直流中的交流成分后才能得到直流电。有的电子设备对电源的质量要求很高,所以…...
生成式人工智能备案办理指南,深度解析大模型备案全流程
早在2023年年初,国家互联网信息办公室、工业和信息化部、公安部针对深度合成服务制定的《互联网信息服务深度合成管理规定》(“《深度合成管理规定》”)顺利施行,其明确了深度合成服务相关方的义务与主体责任,强化了对…...
1527. 患某种疾病的患者
1527. 患某种疾病的患者 题目链接:1527. 患某种疾病的患者 代码如下: # Write your MySQL query statement below select patient_id,patient_name,conditions from Patients where conditions like "DIAB1%" or conditions like "% DI…...
【C/C++】【学生成绩管理系统】深度剖析
可接各类C/C管理系统课设 目录 实现功能 部分1:系统设置和主菜单 1. 引入头文件 2. 定义结构体 3. 函数声明 4. 主函数 部分2:添加学生信息 部分3:删除学生信息 部分4:修改学生信息 部分5:查询学生信息 部分…...
C++作业第四天
#include <iostream> using namespace std; class Per { private: string name; int age; int *high; double *weight; public: //构造函数 Per() { cout << "Per的无参构造" << endl; } Per(str…...
设计模式之工厂方法模式(Factory Method Pattern)
目录 1.1、前言1.2、工厂方法模式简介1.2.1、工厂方法模式的主要特点1.2.2、工厂方法模式的主要结构1.2.3、使用工厂方法模式的好处 1.3、SpringBoot中那些场景使用了工厂方法模式1.4、日常工作中那些业务场景可以使用工厂方法模式1.5、工厂方法模式实战(以某商场一…...
API接口设计的18条规范
API接口设计的18条规范 签名 目的:防止数据被篡改 实现方法: 接口请求方将请求参数、时间戳和密钥拼接成一个字符串使用MD5等hash算法生成签名sign在请求参数或请求头中增加sign参数,传递给API接口API接口网关服务验证传递的sign值&#…...
adb简单使用命令
1. 查看当前连接的设备 adb devices 2. 文件路径 adb install apk 注意文件路径中不能有中文 3. adb shell ps Toplogcatlogcat可以结合greplogcat *:w 显示警告级别以上de 4. adb uninstall 软件名 1.软件名如何查看? 使用命令adb shell pm list packages会…...
构建 deno/fresh 的 docker 镜像
众所周知, 最近 docker 镜像的使用又出现了新的困难. 但是不怕, 窝们可以使用曲线救国的方法: 自己制作容器镜像 ! 下面以 deno/fresh 举栗, 部署一个简单的应用. 目录 1 创建 deno/fresh 项目2 构建 docker 镜像3 部署和测试4 总结与展望 1 创建 deno/fresh 项目 执行命令…...
数据库 | 数据库设计的步骤
1.需求分析 调查机构情况与熟悉业务活动,明确用户的需求,确定系统的边界,生成数据字典和用户需求规格说明书 2.概念结构设计 将从需求分析中得到的用户需求抽象为概念模型,设计E-R模型 3.逻辑结构设计 将E-R图转换为和DBMS相…...
改进YOLO系列 | CVPR 2021 | Involution:超越convolution和self-attention的神经网络算子
Involution:超越卷积和自注意力的新型神经网络算子(中文综述) 简介 Involuton是CVPR 2021上提出的新型神经网络算子,旨在超越卷积和自注意力,提供更高效、更具表达力的特征提取能力。 Involution原理 Involution的…...
落地速度与效果之争,通用VS垂直,我的观点和预测。
标题:AI大模型战场:通用VS垂直,谁将领跑落地新纪元? 摘要:随着人工智能技术的飞速发展,大模型的应用场景日益广泛。在这场竞赛中,通用大模型和垂直大模型各有优势,落地速度和可能性也…...
【Android面试八股文】在Android中,出现ClassNotFound的有可能的原因是什么?
在Android环境下类未找到的可能原因 在Android环境下,类未找到的可能原因包括但不限于以下几点: 类路径问题:Android应用使用的类通常存储在APK文件中。如果类所在的APK文件没有被正确加载,或者应用的类路径配置有误,就会导致类未找到的错误。 多DEX文件加载问题:在一些…...
模板引擎与 XSS 防御
在 View 层,可以解决 XSS 问题。在本书的“跨站脚本攻击”一章中,阐述了“输入检查” 与“输出编码”这两种方法在 XSS 防御效果上的差异。XSS 攻击是在用户的浏览器上执行的, 其形成过程则是在服务器端页面渲染时,注入了恶意的 H…...
vue3轮播图怎么做
先看效果 实现代码 <n-carouseleffect"card"dot-type"line"draggable:autoplay"!isHovered":current-index"currentIndex"prev-slide-style"transform: translateX(-150%) translateZ(-450px);opacity:1"next-slide-st…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
