C语言是结构化的程序设计语言,这里的结构指的是顺序结构、选择结构、循环结构,我们生活中几乎所有事情,比如做饭、打车、网购,都能拆解成这三种基本操作的组合。
我们可以使用if、switch实现分支,使用for、while、do while实现循环
1、if语句
1.1、if
if 语句的语法形式如下:
if(/*表达式*/)
语句
若表达式为真,则执行语句;若为假,则不执行。
在C语言中,0表示假,非0值表示真。当表达式结果为0时,语句不会执行;若结果为非0,则语句将被执行。
#include <stdio.h>
int main() {
int num;
printf("请输入一个整数: ");
scanf("%d", &num);
if (num % 2 != 0) {
printf("%d 是奇数\n", num);
}
return 0;
}
假设用户输入数字5,程序将输出:
5 是奇数
代码流程图如下:
1.2、else
如果一个数,不是奇数,那就是偶数了,这里就需要if...else..语句了:
if (/*表达式*/)
//语句1
else
//语句2
#include <stdio.h>
int main() {
int num;
printf("请输入一个整数: ");
scanf("%d", &num);
if (num % 2 != 0) {
printf("%d 是奇数\n", num);
} else {
printf("%d 是偶数\n", num);
}
return 0;
}
4 是偶数
小练习:写一个C语言代码,输入一个年龄,>=18岁就输出:成年,否则就输出:未成年
#include <stdio.h>
int main() {
int age;
printf("请输入年龄:");
scanf("%d", &age);
if (age >= 18) {
printf("成年\n");
} else {
printf("未成年\n");
}
return 0;
}
1.3、分支中包含多条语句
if...else语句中,默认只控制一条语句,例如:
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if (age >= 18)
printf("成年了\n");
printf("上大学了");
return 0;
}
我们发现,无论我们输入小于18的数字还是输入大于18的数字,控制台都会打印出“上大学了”,因为if默认只是控制一条语句,如果为真就打印“成年了”,如果为假就不打印,而printf("上大学了");是独立存在的,不管if是真是假都会执行!!!如果if要控制多个语句则要加上{ }将代码区域括起来,else同理。
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if (age >= 18)
{
printf("成年了\n");
printf("上大学了");
}
else{
printf("未成年\n")
}
return 0;
}
1.4、嵌套if
在if..else...语句中,else可以和另一个if搭配,构成多重判断:
#include <stdio.h>
int main() {
int num;
printf("请输入一个整数: ");
scanf("%d", &num);
if (num > 0) {
printf("输入的是正数\n");
} else if (num < 0) {//这⾥的if 相当于嵌套在els语句中,形成了嵌套结构
printf("输入的是负数\n");
} else {
printf("输入的是0\n");
}
return 0;
}
1.5、悬空else问题
如果有多个if..else...,else只跟距离他最近的if匹配:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a == 1)
if (b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
当你运行代码的时候会发现什么也没有输出,如果我们希望else和第一个if进行匹配,那么可以加上大括号:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a == 1)
{
if (b == 2)
printf("hehe\n");
}
else
{
printf("haha\n");
}
return 0;
}
2、关系操作符
C语言⽤于比较的表达式,称为“关系表达式”(relationalexpression),里面使用的运算符就称 为“关系运算符”(relationaloperator),主要有下面6个。
2.1、关系运算符列表
C语言中的关系运算符用于比较两个值的大小或相等性,返回结果为布尔值(1
表示真,0
表示假)。以下是6个基本的关系运算符:
>
:大于。例如a > b
,若a
的值大于b
,则返回1
,否则返回0
。<
:小于。例如a < b
,若a
的值小于b
,则返回1
,否则返回0
。>=
:大于等于。例如a >= b
,若a
的值大于或等于b
,则返回1
,否则返回0
。<=
:小于等于。例如a <= b
,若a
的值小于或等于b
,则返回1
,否则返回0
。==
:等于。例如a == b
,若a
的值等于b
,则返回1
,否则返回0
。!=
:不等于。例如a != b
,若a
的值不等于b
,则返回1
,否则返回0
。
2.2、使用示例
#include <stdio.h>
int main() {
int a = 5, b = 3;
printf("a > b: %d\n", a > b); // 输出 1
printf("a < b: %d\n", a < b); // 输出 0
printf("a == b: %d\n", a == b); // 输出 0
return 0;
}
2.3、注意事项
- 区分
=
和==
:=
是赋值运算符,==
是关系运算符。混淆两者会导致逻辑错误。 - 浮点数比较:由于浮点数精度问题,直接使用
==
比较可能不准确,通常用绝对值差小于某个极小值(如1e-9
)代替。
float x = 0.1 + 0.2;
if (fabs(x - 0.3) < 1e-9) { /* 视为相等 */ }
2.4、代码解释
float x = 0.1 + 0.2;
在数学上应该等于0.3,但由于浮点数的存储特性,实际结果可能是类似0.30000000000000004的值。if (fabs(x - 0.3) < 1e-9)
使用极小的容差(10^-9)来判断两个浮点数是否"相等"。fabs()
计算绝对差值,当差值小于阈值时认为两者相等。
2.5、运算符优先级
关系运算符的优先级低于算术运算符(如+
、*
),但高于赋值运算符(如=
)。例如:
int result = 5 + 3 < 10; // 等价于 (5 + 3) < 10,结果为1
3、条件操作符
条件操作符也叫三目操作符,需要接受三个操作数的,形式如下:
exep1 ? exp2 : exp3
条件操作符的计算逻辑是:如果 exp1 为真, exp2 计算,计算的结果是整个表达式的结果;如果exp1为假exp3 计算,计算的结果是整个表达式的结果。
int a = 5, b = 10;
int max = (a > b) ? a : b; // max的值为10
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
if (a > 5)
b = 3;
else
b = -3;
printf("%d\n", b);
return 0;
}
改进后:
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
b = (a > 5) ? 3 : -3;
printf("%d\n", b);
return 0;
}
4、逻辑操作符
4.1、逻辑与 &&
- 功能:当且仅当两个操作数均为非零(真)时,结果为真(1);否则为假(0)。
- 示例:
int a = 5, b = 3; if (a > 0 && b > 0) { // 条件成立,执行此处代码 }
4.2、逻辑或 ||
- 功能:若任意一个操作数为非零(真),结果为真(1);仅当两者均为零(假)时结果为假(0)。
- 示例:
int x = 0, y = 2; if (x > 0 || y > 0) { // 条件成立(y为真),执行此处代码 }
4.3、逻辑非 !
- 功能:对操作数取反。若操作数为非零(真),结果为假(0);若操作数为零(假),结果为真(1)。
- 示例:
int flag = 0; if (!flag) { // flag为假,取反后条件成立,执行此处代码 }
4.4、注意事项
- 短路求值:
&&
和||
会从左至右计算,若左操作数已能确定结果,右操作数不会被计算。int *ptr = NULL; if (ptr != NULL && *ptr == 10) { // 避免解引用空指针 // 安全操作 }
阅读下面代码,判断结果:
#include <stdio.h>
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
//i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}
4.4、闰年判断练习
闰年判断的规则:
- 能被4整除并且不能被100整除是闰年
- 能被400整除是闰年
#include <stdio.h>
int main() {
int year = 0;
scanf("%d", &year);
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
printf("是闰年\n");
} else {
printf("不是闰年\n");
}
return 0;
}
5、switch语句
除了if,在C语言中switch也能实现分支运算。switch的基本语法如下:
switch (表达式) {
case 常量1:
语句块1;
break;
case 常量2:
语句块2;
break;
// 更多case...
default:
默认语句块;
}
注意:switch后面的表达式必须是整型表达式,case后面的值必须是整型常量表达式。
5.1、if和switch的对比
例子,判断一个数是多少,if和switch的对比:
#include <stdio.h>
int main() {
int num = 2;
if (num == 1) {
printf("Number is 1\n");
}
else if (num == 2) {
printf("Number is 2\n");
}
else if (num == 3) {
printf("Number is 3\n");
}
else {
printf("Number is not 1, 2, or 3\n");
}
return 0;
}
#include <stdio.h>
int main() {
int num = 2;
switch (num) {
case 1:
printf("Number is 1\n");
break;
case 2:
printf("Number is 2\n");
break;
case 3:
printf("Number is 3\n");
break;
default:
printf("Number is not 1, 2, or 3\n");
}
return 0;
}
上述的代码中有几点需要注意:
- case后面必须要跟空格
- 每一个case后面的代码块执行完后,需要break才能跳出switch语句,否则会发生击穿现象!
5.2、switch中的break
switch语句的执行逻辑是依次匹配case标签,一旦匹配成功,会从该标签处开始执行,直到遇到break
或switch语句结束。若未显式添加break
,编译器不会自动插入终止逻辑,导致代码继续向下执行。
switch (value) {
case 1:
printf("Case 1\n");
// 无break,发生击穿
case 2:
printf("Case 2\n");
break;
default:
printf("Default\n");
}
若value
为1,输出:
Case 1
Case 2
若value
为2,仅输出:
Case 2
那是不是击穿现象就没有用了呢?不是的,某些情况下击穿现象可以简化代码结构:
例子:输入个1~7的数字,打印对应的星期几:
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d", &day);
switch(day)
{
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期天\n");
break;
default:
printf("输入错误,请输入1-7的数字\n");
}
return 0;
}
如果需求发生变化,变为:
- 输入1-5,输出的是“工作日”;
- 输入6-7,输出“休息日”
#include <stdio.h>
int main() {
int day = 0;
printf("请输入数字1-7: ");
scanf("%d", &day);
switch(day) {
case 1:
case 2:
case 3:
case 4:
case 5:
printf("工作日\n");
break;
case 6:
case 7:
printf("休息日\n");
break;
}
return 0;
}
由此可见,我们可以根据实际需求来自行选择是否利用击穿现象。
5.3、switch语句中的default
当switch
表达式的值不匹配任何case
时,执行default
下的代码块。
#include <stdio.h>
int main() {
int day = 0;
printf("请输入数字1-7: ");
scanf("%d", &day);
switch(day) {
case 1:
case 2:
case 3:
case 4:
case 5:
printf("工作日\n");
break;
case 6:
case 7:
printf("休息日\n");
break;
default:
printf("输入无效,请输入1-7的数字\n");
}
return 0;
}
6、while循环
while (condition) {
// 循环体
}
6.1、while语句的执行流程
while循环的执行步骤
- 计算
condition(判断表达式)
的值。 - 如果
condition
为真,执行循环体内的代码,然后回到步骤1。 - 如果
condition
为假,跳过循环体,继续执行循环之后的代码。
6.2、while循环的实践
在屏幕上打印1-10:
#include <stdio.h>
int main() {
int i = 1;
while (i <= 10) {
printf("%d\n", i);
i++;
}
return 0;
}
6.3、练习
输入一个正的整数,逆序打印这个整数的每一位,例如: 输入:1234,输出:4321 输入:521,输出:125。
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
while (n)
{
printf("%d ", n % 10);
n /= 10;
}
return 0;
}
7、for循环
7.1、语法形式
for循环是三种循环中使用最多的,其语法结构如下:
for (初始化表达式; 条件表达式; 更新表达式) {
// 循环体
}
- 初始化表达式(表达式1):在循环开始时执行一次,通常用于初始化循环变量。
- 条件表达式(表达式2):每次循环前检查,若为真则执行循环体,否则退出循环。
- 更新表达式(表达式3):每次循环后执行,通常用于更新循环变量。
7.2、for循环执行流程图
7.3、小练习
在屏幕上打印1-10:
#include <stdio.h>
int main() {
for (int i = 1; i <= 10; i++) {
printf("%d ", i);
}
return 0;
}
8、do-while循环
8.1、语法结构
do {
// 循环体代码
} while (条件表达式);
8.2、代码流程图
8.3、do-while与while循环的区别
- while循环:先检查条件,再决定是否执行循环体(可能一次都不执行)。
- do-while循环:先执行循环体,再检查条件(至少执行一次)。
// while循环(可能不执行)
int i = 5;
while (i < 5) {
printf("%d ", i); // 不输出
i++;
}
// do-while循环(至少执行一次)
int j = 5;
do {
printf("%d ", j); // 输出5
j++;
} while (j < 5);
8.4、典型应用场景
输入验证:确保用户至少输入一次数据。
int num;
do {
printf("输入正数: ");
scanf("%d", &num);
} while (num <= 0);
菜单驱动程序:显示菜单并处理用户选择,直到退出。
char choice;
do {
printf("1. 开始\n2. 退出\n");
scanf(" %c", &choice);
// 处理选择逻辑
} while (choice != '2');
while(条件)后面必须加分号,否则编译器会报错
9、break和continue语句
9.1、break语句
break语句用于立即终止当前所在的循环(for、while、do-while)或switch语句,并跳出该结构继续执行后续代码。通常用于满足特定条件时提前结束循环。
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // 当i等于5时跳出循环
}
printf("%d ", i);
}
// 输出:0 1 2 3 4
9.2、continue语句
continue语句用于跳过当前循环的剩余部分,直接进入下一次循环的条件判断(for、while、do-while)。通常用于忽略某些特定条件的迭代。
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue; // 跳过偶数
}
printf("%d ", i);
}
// 输出:1 3 5 7 9
9.3、关键区别
- break:完全终止循环,后续代码不再执行。
- continue:仅跳过当前迭代,循环继续执行下一次迭代。
10、循环的嵌套
找出100~200之间的素数,并打印在屏幕上。注:素数⼜称质数,只能被1和本⾝整除的数字。
#include <stdio.h>
int main()
{
int i = 0;
//循环产生100~200之间的数字
for (i = 100; i <= 200; i++)
{
//判断i是不是素数
int j = 0;
int flag = 1;
//假设i是素数,flag就为1,否则flag设置为0
//非零整数不会被比自己大的数整除,所以我们选2~i-1之间的数字进行计算
//循环产⽣2~i - 1之间的数字
for (j = 2; j < i; j++)
{
if (i % j == 0)
{
flag = 0;//flag为0,就退出循环,因为肯定不是素数
break;
}
}
if (flag == 1)
printf("%d ", i);//如果flag没变还是1,那么这个数就是素数,进行下一轮循环
}
return 0;
}
11、goto语句
在C语言中,goto
语句是一种无条件跳转语句,用于将程序控制流转移到同一函数内的指定标签处。语法格式为:
goto label;
...
label: statement;
#include <stdio.h>
int main()
{
printf("hehe\n");
goto next;
printf("haha\n");
next:
return 0;
}
goto 语句如果使用的不当,就会导致在函数内部随意乱跳转,打乱程序的执行流程,所以尽量不要使用goto语句。但goto语句并不是一无是处,当需要从嵌套循环中直接退出时,goto
可以简化代码逻辑,避免设置多个标志变量:
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (error_condition) goto cleanup;
}
}
cleanup: printf("Exited loops");