文章目录
分支与循环
C语言是结构化的程序设计语言,其中的结构指的是顺序结构、选择结构、循环结构,C语言是能够实现这三种结构的,我们如果仔细分析,我们日常所见的事情都可以拆分为这三种结构或者这三种结构的组合。我们可以使用 if 、 switch 实现分支结构,使用for 、 while 、 do while 实现循环结构。
1.if语句
1.1 if 语法形式
最基本的表达式如下:
if(表达式)
语句1;
其中表达式成立(为真),则语句执行;表达式不成立(为假),则语句不执行在C语言中0为假,非0表示真,也就是表达式的结果如果是0,则语句不执行,表达式的结果如果是不是0,则语句执行。
// 例⼦:输⼊⼀个整数,判断是否为奇数
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
if (num % 2 == 1)
printf("%d 是奇数\n", num);
return 0;
}
1.2 else
如果⼀个数不是奇数,那就是偶数了,如果任意⼀个整数,我们要清楚的判断是奇数还是偶数怎么表式呢?这里就需要 if…else… 语句了,语法形式如下:
if ( 表达式 )
语句1
else
语句2
//例⼦:输⼊⼀个整数,判断是否为奇数,如果是奇数打印是奇数,否则打印数偶数。
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
if(num % 2 == 1)
printf("%d 是奇数\n", num);
else
printf("%d 是偶数\n", num);
return 0;
}
1.3 分支中包含多条语句
默认在 if 和 else 语句中默认都只控制⼀条语句,因此需要控制多条语句时应该使用大括号 { } 把代码括起来,不然会出现不考虑if语句条件的真假都会执行,例如:
#include <stdio.h>
int main()
{
float time = 0;
scanf("%f", &time);
if (time > 2.5)
printf("练习时长超两年半\n");
printf("个人练习生\n");
return 0;
}
上面的代码你会发现无论有没有时长超两年半都会输出个人练习生
把大括号 { } 把那两条语句括起来就不会出现这种情况。
1.4 嵌套if (else if)
在 if else 语句中, else 可以与另⼀个 if 语句连用,构成多重判断。
比如:要求输入⼀个整数,判断输入的整数是0还是正数或者负数。请看如下代码:
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
if(num == 0)
printf("输⼊的数字是0\n");
else if(num > 0) //这⾥的if 相当于嵌套在else语句中,形成了嵌套结构
printf("输⼊的数字是正数\n");
else
printf("输⼊的数字是负数\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;
}
最终结果是啥都不打印,为什么呢?
因为当出现有多个 if 和 else ,可以记住这样一条规则, else 总是跟最接的 if 匹配。
上⾯的代码排版,让 else 和第⼀个 if 语句对齐,让我们以为 else 是和第⼀个if匹配的,当 if语句不成立的时候,自然想到的就是执行 else 子句,打印 haha
但实际上 else 是和第二个 if进行匹配的,这样后边的 if…else 语句是嵌套在第一个 if 语句中的,如果第⼀个 if 语句就不成立,嵌套 if 和 else 就没机会执行了,最终啥都不打印。
//加上大括号 { } ,改成这样就好理解了
#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 语言用于比较的表达式,称为 “关系表达式”(relational expression),里面使用的运算符就称为“关系运算符”(relational operator),主要有下面6个。
• > ⼤于运算符
• < ⼩于运算符
• >= ⼤于等于运算符
• <= ⼩于等于运算符
• == 相等运算符
• != 不相等运算符
关系表达式通常返回 0 或 1 ,表⽰真假。
C 语言中, 0 表示假,所有非零值表示真。比如, 20 > 12 返回 1 , 12 > 20 返回 0 。
关系表达式常用于 if 或 while 结构。
注意要点1
比如在if(n = 3) 表示的是n 赋值 3 返回 1 表示真 ,所以if 总是判断为真。
而 if (n == 3) 是判断当 n 等于 3 的情况下为真。
(为了防止出现这种错误,有的程序员喜欢将变量n写在等号的右边)
注意要点2
另⼀个需要避免的错误是:多个关系运算符不宜连用。
//这是合法表达式,不会报错,但是通常达不到想要的结果
a < b < c
//因为关系运算符是从左到右计算,所以实际执⾏的是下⾯的表达式
(a < b) < c
//上⾯式⼦中,a < b返回 0 或 1 ,所以最终是 0 或 1 与变量 c 进⾏⽐较。
//如果想要判断变量 j的值是否在 a 和 c 之间,应该使⽤下⾯的写法。
a < b && b < c
//⽐如:我们输⼊⼀个年龄,如果年龄在18岁~36岁之间,我们输出⻘年。
如果我们这样写
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if(18<=age<=36)
{
printf("⻘年\n");
}
return 0;
}
当我们输入10的时候,依然输出青年,如下图:
我们先拿18和age中存放的10比较,表达式18<=10为假, 18<=age 的结果是0,再拿0和36比较,0<=36为真,所以打印了 青年 ,所以即使当age是10的时候,也能打印青年 ,逻辑上是有问题,这个代码应该写成如下:
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if(18<=age && age<=36)
{
printf("⻘年\n");
}
return 0;
}
3. 逻辑操作符:&& , || , !
逻辑运算符提供逻辑判断功能,⽤于构建更复杂的表达式,主要有下面三个运算符。
• ! :逻辑取反运算符(改变单个表达式的真假)。
• && :与运算符,就是并且的意思(两侧的表达式都为真,则为真,否则为假)。
• || :或运算符,就是或者的意思(两侧⾄少有⼀个表达式为真,则为真,否则为假)。
3.1 逻辑取反运算符
- ! 的用法
比如,我们有⼀个变量叫 flag ,如果flag为假,要做⼀个什么事情,就可以这样写代码:
if(!flag)
{
printf("do something\n");
}
3.2 与运算符
- && 的用法
比如:如果我们说月份是3月到5月,是春天,下面使用代码来体现:
#include <stdio.h>
int main()
{
int month = 0;
scanf("%d", &month);
if(month >= 3 && month <= 5)
{
printf("春季\n");
}
return 0;
}
3.3 或运算符
- || 的用法
比如,一年中12月份、一月份和二月份为冬天,这时用 && 就不好体现了,就需要用到 || 双目操作符,代码如下:
#include <stdio.h>
int main()
{
int month = 0;
scanf("%d", &month);
if(month == 12 || month==1 || month == 2)
{
printf("冬季\n");
}
return 0;
}
3.4 短路 *
C语言逻辑运算符还有⼀个特点,它总是先对左侧的表达式求值,再对右边的表达式求值,这个顺序是保证的。如果左边的表达式满足逻辑运算符的条件,就不再对右边的表达式求值。这种情况称为“短路”。
首先在 && 上的体现
if(month >= 3 && month <= 5)
从左往右进行判断时当左操作数 month >= 3 的结果是0为假的时候,也就不会再往后进行判断,及时不判断 month <= 5 ,整个表达式的结果也是0(不是春季)。
其次在 || 的体现
if(month == 12 || month==1 || month == 2)
如果month == 12,则不⽤再判断后面的month是否等于1或者2,整个表达式的结果也是1(是冬季)。所以, || 操作符的左操作数的结果不为0时,就无需执行右操作数。
4. switch语句
除了 if 语句外,C语言还提供了 switch 语句来实现分支结构。switch 语句是⼀种特殊形式的 if…else 结构,用于判断条件有多个结果的情况。它把多重的 else if 改成更易用、可读性更
好的形式.
4.1 语法形式
switch (expression)
{
case value1: statement
case value2: statement
default: statement
}
注意:
- switch 后的 expression 必须是整型表达式
- case 后的值,必须是整形常量表达式
例子:输⼊任意⼀个整数值,计算除3之后的余数
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
switch(n%3)
{
case 0:
printf("整除,余数为0\n");
break;
case 1:
printf("余数是1\n");
break;
case 2:
printf("余数是2\n");
break;
}
return 0;
}
上述代码需要注意的有:
- case 和后边的数字之间必须有空格
- 每⼀个 case 语句中的代码执行完成后,需要加上 break ,才能跳出这个switch语句
4.2 switch语句中的break
前⾯的代码中,如果我们去掉case语句中的break,会出现什么情况呢?
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
switch(n%3)
{
case 0:
printf("整除,余数为0\n");
case 1:
printf("余数是1\n");
case 2:
printf("余数是2\n");
}
return 0;
}
结果:
我们发现,7除以3本来余数是1,但是我们发现程序运行的结果多了一行“余数是2”的打印。
原因是 switch 语句也是分支效果的,只有在 switch 语句中使用 break 才能在跳出 switch 语句,如果某⼀个 case 语句的后边没有 break 语句,代码会继续玩下执行,有可能执行其他 case语句中的代码,直到遇到 break 语句或者 switch 语句结束。就比如上面的代码就执行了 case 2 中的语句。因此对于 switch 来说 break 语句很重要。
当然, break 也不是每个 case 语句都得有,这就得根据实际情况来看了。
例如:在输⼊⼀个1~7的数字中
1.输⼊1-5,输出的是“工作日”;
2. 输⼊6-7,输出“休息日”。
#include <stdio.h>
int main()
{
int day = 0;
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;
}
因此需根据实际情况来使用 break 语句完成自身的需求。
4.3 switch语句中的default
在使用 switch 语句的时候,我们经常可能遇到⼀种情况,比如 switch 后的表达式中的值无法匹配代码中的 case 语句的时候,这时候要不就不做处理,要不就得在 switch 语句中加入
default 子句。也就是 switch 后边的 expression 的结果不是 value1 ,也不是 value2 的时候,就会执行default 子句。
switch (day)
{
case 1:
case 2:
...
default:
printf("输入错误请在1~7之间输入\n");
break;
}
在 switch 语句中 case 子句和 default 子句有要求顺序吗? default 只能放在最后吗?
其实,在 switch 语句中 case 语句和 default 语句是没有顺序要求的,只要你的顺序是满足实际需求的就可以,不过我们通常是把 default 子句放在最后处理的。
5. while循环
5.1 while 语法形式:
while(表达式)
语句;//如果循环体想包含更多的语句,可以加上⼤括号
5.2 while语句的执行流程
首先上来就是执行判断表达式,表达式的值为0,循环直接结束;表达式的值不为0,则执行循环语句,语句执行完后再继续判断,是否进行下⼀次判断。
5.3 while循环的实践
例子:
输⼊⼀个正的整数,逆序打印这个整数的每⼀位
例如:
输⼊:1234,输出:4 3 2 1
输⼊:521,输出:1 2 5
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
while(n)
{
printf("%d ", n%10);
n /= 10;
}
return 0;
}
解析:
- 要想得到n的最低位,可以使⽤n%10的运算,得到的余数就是最低位,如:1234%10得到4
- 要想去掉n的最低位,找出倒数第⼆位,则使⽤ n=n/10 操作就可以去掉最低位的,如:n=1234/10得到123,123相较于1234就去掉了最低位,123%10就得到倒数第⼆位3。
- 循环1和2两个步骤,在n变成0之前,就能到所有的位
6. for循环 (常用)
6.1 语法形式
for 循环是三种循环中使⽤最多的, for 循环的语法形式如下:
for(表达式1; 表达式2; 表达式3)
语句;//如果循环体想包含更多的语句,可以加上⼤括号
表达式1 用于循环变量的初始化
表达式2 用于循环结束条件的判断
表达式3 用于循环变量的调整
6.2 for循环的执行流程
首先执行 表达式1 初始化循环变量,接下来就是执行 表达式2 的判断部分, 表达式2 的结果如果 ==0,则循环结束; 表达式2 的结果如果!=0则执⾏循环语句,循环语句执行完后,再去执行 表达式3 ,调整循环变量,然后再去 表达式2 的地方执行判断, 表达式2 的结果是否为0,决定循环是否继续。整个循环的过程中,表达式1初始化部分只被执行1次,剩下的就是表达式2、循环语句、表达式3在循环。
6.3 for循环的实践
在屏幕上打印1~10的值
#include <stdio.h>
int main()
{
int i = 0;
for(i=1; i<=10; i++)
{
printf("%d ", i);
}
return 0;
}
进阶练习计算1~100之间3的倍数的数字之和
#include <stdio.h>
int main()
{
int i = 0;
int sum = 0;
for(i=1; i<=100; i++)
{
if(i % 3 == 0)
sum += i;
}
printf("%d\n", sum);
return 0;
}
//⼩⼩的优化
//如果能直接产⽣3的倍数的数字就省去了多余的循环和判断,更加高效
#include <stdio.h>
int main()
{
int i = 0;
int sum = 0;
for(i=3; i<=100; i+=3)
{
sum += i;
}
printf("%d\n", sum);
return 0;
}
7. do-while循环
7.1 语法形式
在循环语句中 do while 语句的使⽤最少,它的语法如下:
do
语句;
while(表达式);
while 和 for 这两种循环都是先判断,条件如果满足就进入循环,执行循环语句,如果不满足就跳出循环;而 do while 循环则是先直接进入循环体,执行循环语句,然后再执行 while 后的判断表达式,表达式为真,就会进行下⼀次,表达式为假,则不再继续循环。
7.2 do while循环的执行流程
在 do while 循环中先执行图上的“语句”,执行完语句,在去执行“判断表达式”,判断表达式的结果是!=0,则继续循环,执行循环语句;判断表达式的结果==0,则循环结束。
所以在 do while 语句中循环体是至少执行⼀次的,这是 do while 循环比较特殊的地方。
7.3 do while循环的实践
在屏幕上打印1~10的值
#include <stdio.h>
int main()
{
int i = 1;
do{
printf("%d ", i);
i = i + 1;
}while(i<=10);
return 0;
}
⼀般 do while 使用在循环体至少被执行一次的场景下,所以较少⼀些。
拓展
输⼊⼀个正整数,计算这个整数是几位数?
例如:
输⼊:1234 输出:4
输⼊:12 输出:2
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int cnt = 0;
do{
cnt++;//1.首先执行第一条语句此时 cnt = 1
n = n / 10; //2.其次再对 n 重新赋值
} while (n);//3.最后进行表达式的判断
printf("%d\n", cnt);
return 0;
}
上述do while 语句的优点已经体现出来,就算输入的是一个0,也能读取为一位数。
8. break和continue语句
在循环执行的过程中,如果某些状况发生的时候,需要提前终止循环,这是非常常见的现象。C语言中提供了 break 和 continue 两个关键字,就是应该到循环中的。
- break 的作用是用于永久的终止循环,只要 break 被执行,直接就会跳出循环,继续往后执行。
- continue 的作用是跳过本次循环 continue 后边的代码,在 for 循环和 while 循环中有所
差异的。
8.1 while循环中的 break 和 continue
8.1.1 break举例
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
if(i == 5)
break;//当i等于5后,就执⾏break,循环就终⽌了
printf("%d ", i);
i = i+1;
}
return 0;
}
结果:
打印了1,2,3,4后,当i等于5的时候,循环正 break 的地方终止,不再打印,不再循环。
所以 break 的作用就是永久的终止循环,只要 break 被执行, break 外的第⼀层循环就终止了。那以后我们在循环中,想在某种条件下终止循环,则可以使用 break 来完成我们想要的效果。
8.1.2 continue举例
continue 是继续的意思,在循环中的作用就是跳过本次循环中 continue 后边的代码,继续进行下一次循环的判断。
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
if(i == 5)
continue;
//当i等于5后,就执⾏continue,直接跳过continue的代码,去循环的判断的地⽅
//因为这⾥跳过了i = i+1,所以i⼀直为5,程序陷⼊和死循环
printf("%d ", i);
i = i+1;
}
return 0;
}
到这里我们就能分析出来, continue 可以帮助我们跳过某一次循环 continue 后边的代码,直接到循环的判断部分,进行下一次循环的判断,如果循环的调整是在 continue 后边的话,可能会造成死循环。
8.2 for循环中的 break 和 continue
8.2.1 break举例
其实和 while 循环中的 break 一样, for 循环中的 break 也是用于终止循环的,不管循环还需要循环多少次,只要执行到了 break ,循环就彻底终止,我们上代码。
#include <stdio.h>
int main()
{
int i = 1;
for(i=1; i<=10; i++)
{
if(i == 5)
break;
printf("%d ", i);
}
return 0;
}
结果:
8.2.2 continue举例
上面的代码,如果把 break 换成 continue 会是什么结果呢?
#include <stdio.h>
int main()
{
int i = 1;
for(i=1; i<=10; i++)
{
if(i == 5)
continue;//这⾥continue跳过了后边的打印,来到了i++的调整部分
printf("%d ", i);
}
return 0;
}
结果:
所以在 for 循环中 continue 的作用是跳过本次循环中 continue 后的代码,直接去到循环的调整部分。未来当某个条件发生的时候,本次循环无需再执行后续某些操作的时候,就可以使用 continue 来实现。
8.3 do while循环中的 break 和 continue
do.while 语句中的 break 和 continue 的作用和 while 循环中几乎⼀模⼀样。
9. 循环的嵌套 *
前面学习了三种循环 while , do while , for ,这三种循环往往会嵌套在⼀起才能更好的解
问题,就是我们所说的:循环嵌套,这里我们就看一个例子。
找出100~200之间的素数,并打印在屏幕上。
注:素数⼜称质数,只能被1和本⾝整除的数字。
#include <stdio.h>
int main()
{
int i = 0;
//循环产⽣100~200的数字
for(i=100; i<=200; i++)
{
//判断i是否为素数
//循环产⽣2~i-1之间的数字
int j = 0;
int flag = 1;//假设i是素数
for(j=2; j<i; j++)
{
if(i % j == 0)
{
flag = 0;
break;
}
}
if(flag == 1)
printf("%d ", i);
}
return 0;
}
解析:
- 要从100 ~ 200之间找出素数,首先得有100~200之间的数,这里可以使用循环解决。
- 假设要判断 i 是否为素数,需要拿2 ~ i -1之间的数字去试除 i,需要产生2 ~ i -1之间的数字,也可以使用循环解决。
- 如果2 ~ i - 1之间有数字能整除 i ,则 i 不是素数,如果都不能整除,则 i 是素数。
10. goto语句
C语言提供了⼀种非常特别的语法,就是 goto 语句和跳转标号, goto 语句可以实现在同⼀个函数内跳转到设置好的标号处。
例如:
#include <stdio.h>
int main()
{
printf("hehe\n");
goto next:
printf("haha\n");
next:
printf("跳过了haha的打印\n");
return 0;
}
结果:
goto 语句如果使用的不当,就会导致在函数内部随意乱跳转,打乱程序的执行流程,所以我们的建议是能不用尽量不去使用;但是 goto 语句也不是一无是处,在多层循环的代码中,如果想快速跳出使用 goto 就非常的方便了。
for(...)
{
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
}
error:
...
本来 for 循环想提前退出得使用 break ,一个 break 只能跳出一层 for 循环,如果3层循环嵌套就得使用3个 break 才能跳出循环,所以在这种情况下我们使用 goto 语句就会更加的快捷。