🚀🚀🚀大家好,我是FLASH。这篇博客会为大家详细讲解C语言中分支与循环一节内容,近万字详解,如果大家喜欢我的作品,请留下你的一键三连,这也是对我最大的鼓舞🚀🚀🚀
目录
C语言是结构化的程序设计语言。
C语言设计的初衷就是为了方便大家的生活,那就代表C语言就有能力解决生活中的事情,而我们发现生活中的所发生的事情无非就是三种结构组成:顺序结构,选择结构和循环结构。C语言恰恰抽象了这些结构所对应的语句,接下来这篇博客将会为大家细致的介绍。
C语言为这三种结构都设置了专门的语法:
比如:在C语言中,顺序结构就是的从程序开始一直到程序结束,代码一行接一行实现的情况。
来实现选择结构的语法有:if 语句 和 switch case 语句;
实现循环结构则有:while 循环,for 循环 和 do while 循环;
⚡1.语句
C语句有五大分类,分别为:
表达式语句
函数调用语句
控制语句
复合语句
空语句
这篇博客主要为大家介绍控制语句。控制语句适用于控制程序的执行流程,以实现程序的各种结构方式(C语言支持的三种结构:顺序结构,选择结构,循环结构),它们由特定的语句组成,而C语言有九种控制语句,可分为下列三类:
分支语句(条件判断语句):if 语句、switch 语句;
循环语句: while 语句、for 语句、do while 语句;
转向语句: break 语句、goto 语句、continue 语句、return 语句;
⚡2.分支语句(选择结构)
2.1 if语句
if语句的语法结构可以分为单分支结构,双分支结构和多分支结构。
单分支 双分支 多分支
if(判断表达式) if(判断表达式) if(判断表达式1)
{ { {
语句列表; 语句列表1; 语句列表1;
} } }
else else if(判断表达式2)
{ {
语句列表2; 语句列表2;
} }
else
{
语句列表3;
这里的一对{}称为一个代码块。 }
在这里补充一下,便于下文的理解:如果表达式的结果为真,则语句执行;在C语言中,0表示假,非0表示真。
其中在单分支结构中:若判断表达式结果为真,则执行if中的语句;若判断表达式的结果为假,不执行if中的语句,按照源程序的顺序接着执行。示例如下:
双分支结构中:若判断表达式结果为真,则执行if后大括号内部的语句1;若判断表达式的结果为假,执行else中的语句2。示例如下:
多分支结构中:若判断表达式1的结果为真,则执行if后大括号内部的语句1;若判断表达式1的结果为假,则进入判断表达式2,若结果为真,执行语句2,若结果为假,执行else中的语句3;再次说明,多分支的分支个数并不止图例中3个,可能更多,请读者根据实际情况进行操作。示例如下:
在这里再对多分支语句进行强调,也是非常重要的一点,无论大家在使用多分支时在 if 和 else 中间使用多少 else if , 它归咎到底还是一条语句,只会进入符合条件的那一个选项中。其次不建议大家将 else if 写为 if (根据题目要求进行理性选择),虽然效果相同,但是在程序真正执行时 if--else if--else语句最少判断一次,最多判断(假设有3个else if)4次,而使用 if 替代 else if 后,无论判断表达式为真或为假,它都要进去判断一次,及判断次数永远包含所有情况,判断效率大大下降,希望大家能够理解。
2.1.1 悬空else
else 与离它最近的 if 匹配。
适当地使用{}可以使代码的逻辑更清楚,避免一些阅读程序的误会。
代码风格非常重要,好的代码风格不但可以让阅读代码的读者清晰的理解,而且可以使自己的思维逻辑更加清晰不容易出错,推荐书籍《高质量的C/C++编程》。
简单句两个例子(两个例子中的代码1和代码2输出结果均相同):
2.1.2 if书写形式的对比
用{}使代码误读率下降。
代码1 代码2
if(判断表达式) if(判断表达式)
{ {
return x; return x;
}
else }
{
return y; return y;
}
//推荐 //不推荐
在比较变量和常量是否相等时,写为常量==变量的形式,可以有效避免一些代码问题。
代码1 代码2
int num = 1 ; int num = 1;
if(5 == num) if(num == 5)
{ {
printf("JX\n"); printf("JX\n");
} }
//推荐 //不推荐
2.1.3 return 语句
return 后面的表达式的值就是函数的返回值(字符,整形,浮点型,地址等),使用 return 语句的另一个作用是,终止函数并把控制返回给主函数的下一条语句。
例如下图所示的程序: return 1 / return 2 语句导致printf("JX3\n")永远不会被执行,并终止了test()函数,把下一次的控制权交给主函数,执行下一条语句printf("JX2\n")。
2.1.4 if 语句的练习
(1) 试判断一个数是否为奇数;
提示:奇数☞不能被2整除。
判断一个数是否为奇数
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d",n);
if(n%2 == 1)
{
printf("奇数",n);
}
return 0;
}
(2) 输出1~100之间的奇数;
提示:现在你已经掌握了如何判断一个数是否为奇数,现在你可以思考一下1~100之间的数应该怎样来实现呢?
输出1~100之间的奇数
#include<stdio.h>
int main()
{
int i = 1;
while(i <= 100)
{
if(i % 2 ==1)
{
printf("%d",i);
}
i++;
}
return 0;
}
2.2 switch 语句
使用三目操作符和 if-else 语句很容易就可以写出二选一的程序。然而,有时程序需要在多个选项中进行选择,虽然可以使用 if-else if-else 来完成,但大多数情况下 switch 语句似乎更加适合。
switch的构造如下:
switch 在圆括号中的表达式值是一个整数值(包括char类型),case 标签必须是整数类型(包括char类型)的常量或整形常量表达式,不可以用变量来作为 case 的标签。
switch(整形表达式)
{
case 常量1:
语句;
case 常量2:
语句;
default:
语句;
}
2.2.1 在switch语句中的 break
事实上 switch 语句没办法直接实现分支,需要搭配break使用才能实现真正的分支:
#include <stdio.h> #include<stdio.h>
int main() int main()
{ {
int day = 0; int day = 0;
scanf("%d",&day); scanf("%d",&day);
switch(day) switch(day)
{ {
case 1: case 1:
printf("星期一\n"); case 2:
break; case 3:
case 2: case 4:
printf("星期二\n"); case 5:
break; printf("周内\n");
case 3: break;
printf("星期三\n"); case 6:
break; case 7:
case 4: printf("周末\n");
printf("星期四\n"); break;
break; default:
case 5: printf("输入错误\n");
printf("星期五\n"); break;
break; }
case 6: return 0;
printf("星期六\n"); }
break;
case 7:
printf("星期天\n");
break; 好习惯:
default: < —————— 在 switch 语句中都放一条default子句,
printf("选择错误\n"); 可以在后面加一个break。
break;
}
return 0;
}
好习惯:在最后一个 case 语句的后面加上一条 break语句。
(之所以这么写是可以避免出现在以前的最后一个 case 语句后面忘了添加 break语句)。
break语句实际效果是把语句列表划分为不同的分支部分。break 语句是 switch 语句的出口,让程序离开 switch 语句,跳转到其下一条语句,如果没有 break 语句,就会从匹配标签的第一个case开始一直执行到 switch 末尾。
顺便总结一下,break 语句既可以用于循环语句中同时也可以用于 switch 语句中,但 continue 只能用于循环中。如果 switch 语句在一个循环中,continue 就可以作为 switch 语句的一部分,就像在其他循环一样,continue 让程序跳出循环的剩余部分,包括 switch 语句的其他部分。
2.2.2 default子句
如果表达的值与所有的case标签的值都不匹配怎么办?
其实也没什么,结构就是所有的语句都被跳过而已。
程序并不会终止,也不会报错,因为这种情况在C中并不认为是个错误。
但是,如果你并不想忽略不匹配所有标签的表达式的值时该怎么办呢?
你可以在语句列表中增加一条default子句,把下面的标签
default:
写在任何一个 case 标签可以出现的位置。
当 switch 表达式的值并不匹配所有 case 标签的值时,这个 default 子句后面的语句就会执行。
所以,每个switch语句中只能出现一条default子句。
但是它可以出现在语句列表的任何位置,而且语句流会像执行一个case标签一样执行default子句。
好习惯:在每个 switch 语句中都放一条default子句是个好习惯,甚至可以在后边再加一个 break 。
2.2.3 阅读代码,提升理解
#include <stdio.h> switch允许嵌套使用
int main()
{
int n = 1;
int m = 2;
switch (n)
{
case 1:
m++;
case 2:
n++;
case 3:
switch (n)
{
case 1:
n++;
case 2:
m++;
n++;
break;
}
case 4:
m++;
break;
default:
break;
}
printf("m = %d, n = %d\n", m, n);
return 0;
}
最终结果为 m = 5, n = 3,你答对了吗?
⚡3.循环语句
3.1 while 循环
while的语法结构: while(判断表达式)
{
循环语句;
}
具体实现为,如果判断表达式为真,执行循环语句一次,然后返回判断表达式再次判断;在判断表达式为假之前,循环的判断和执行一直重复进行,每一次循环都被称为一次迭代。图解如下
了解了while的工作流程,就来实现一下在屏幕打印1~10的数字。
3.1.1 while语句中的break和continue
通过代码执行结果看当 i==5 后进入循环,break立即终止 while 循环,continue跳过输出5并且死循环了下去。
由此总结一下:
break在while循环中的作用:
其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。所以,while中的break是用于永久终止循环的。
continue在while循环中的作用:
continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分。进行下一次循环的入口判断。
3.1.2 getchar和putchar
截至目前,学过的大多数程序示例都要求输入数值。相信大家都已经非常熟悉如何使用 scanf() 和 printf() 根据 %c 转换说明读写字符,接下来我将为大家介绍一对字符输入/输出函数:getchar() 和putchar() 。
getchar() 函数不带任何参数,它从输入列表中返回下一个字符。比如下面的语句读取下一个字符输入,并把该字符的值赋给变量ch:
ch = getchar();
该语句与下面的语句效果相同:
scanf("%c",&ch);
putchar()函数打印它的参数。比如下面的语句把之前赋给ch的值作为字符打印出来:
putchar(ch);
该语句与下面的语句效果相同:
printf("%c",ch);
由于 getchar() 和 putchar() 这两个函数只处理字符,所以它们比通用的 scanf() 和 printf() 函数更快、更简洁。并且这两个函数不需要转换说明,因为它们只处理字符。它们定义在 stdio.h 头文件中,比较特殊的是,getchar() 和 putchar() 通常是预处理宏,不是真正的函数,以后我会为大家详细解释哦,这里只做一个大致了解。
下来结合题目来深入了解while循环,scanf(),printf(),getchar(),putchar()。
在题目中有几点要说明一下:
1.scanf()函数默认是读取到空白字符停止或在结尾处停止(其中键盘敲击的\n不会被读取)。
2.getchar() 和 putchar() 一次只读取一个字符,并且将其转化为 int 类型(对应ASCLL码值)赋给ch。
3.2 for循环
for(变量初始化; 判断表达式; 调整变量)
{
循环语句;
}
whlie 循环和 for 循环的对比:
可以发现在 while 循环和 for 循环中均存在循环的三个必须条件,但是如果程序稍微复杂一些就很可能使得while循环的三个必须条件变得分散。这样就使得后期查找修改就不够集中和方便。所以,for 循环的风格更胜一筹;for 循环使用的频率也最高。
在 for 循环中可以省略变量初始化,判断,调整三个部分,但是不建议初学者采用,容易出现循环出现不可控的现象。
3.3 do while循环
do
{
循环语句;
}
while(判断表达式);
do while 循环的特点是至少执行一次,但使用的场景有限,所以不是经常使用。
使用 do while 循环在屏幕上打印1~10。
3.4 经典例题
1. 计算 n的阶乘。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int n = 0;
int i = 0;
int ret = 1;
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
ret = ret * i;
}
printf("%d", ret);
return 0;
}
2. 计算 1!+2!+3!+……+10!
代码一
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int n = 0;
int i = 0;
int j = 0;
int sum = 0;
scanf("%d", &n);
for(i=1;i<=n;i++)
{
int ret = 1;
for (j = 1; j <= i; j++)
{
ret = ret * j;
}
sum = sum + ret;
}
printf("%d",sum);
return 0;
}
代码二
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int n = 0;
int i = 0;
int j = 0;
int sum = 0;
int ret = 1;
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
ret = ret * i;
sum = sum + ret;
}
printf("%d", sum);
return 0;
}
3. 在一个有序数组中查找具体的某个数字n。(用二分查找)
使用二分查找的前提是数组有序。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;
int sz = sizeof(arr) / sizeof(arr[0]);
int left = 0;
int right = sz - 1;
int flag = 0;
scanf("%d", &k);
while (left <= right)
{
int mid = left + (right - left) / 2;
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
printf("找到了,下标为:%d\n", mid);
flag = 1;
break;
}
}
if (flag == 0)
{
printf("找不到");
}
return 0;
}
4. 编写代码,演示多个字符从两端移动,向中间汇聚。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<windows.h>
int main()
{
char arr1[] = "hello world!";
char arr2[] = "************";
int left = 0;
int right = strlen(arr1) - 1;
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000); //单位是毫秒
system("cls"); //作用是清空屏幕
left++;
right--;
}
printf("%s\n", arr2);
return 0;
}
5. 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成功,如果三次均输入错误,则退出程序)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i = 0;
int flag = 0;
char password[20] = { 0 };
for (i = 0; i < 3; i++)
{
printf("请输入密码:");
scanf("%s", password);
if (strcmp(password, "abcdef") == 0)
{
flag = 1;
printf("登陆成功");
break;
}
else
{
printf("密码错误\n");
}
}
if (0 == flag)
{
printf("三次均输入错误,退出程序");
}
return 0;
}
⚡4. goto语句
C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。
例如:一次跳出两层或多层循环。
多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。
4.1 示例:关机程序
shutdown -s -t 60 : 系统自带的关机程序(倒计时60秒)
shutdown -a : 系统自带的取消关机程序
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
system("shutdown -s -t 60");
char arr[20] = { 0 };
again:
printf("电脑将在1分钟内关机,如果输入:廾匸,就取消关机\n请输入:");
scanf("%s", arr);
if (strcmp(arr, "廾匸") == 0)
{
system("shutdown -a");
}
else
{
goto again;
}
return 0;
}