总言
C语言:分支循环。
文章目录
- 总言
- 1、分支语句
- 1.1、if语句
- 1.1.1、基本格式
- 1.1.2、逻辑真假与悬空else
- 1.1.3、练习
- 1.2、switch语句
- 1.2.1、基本格式:break、case、default
- 1.2.2、练习:switch语句嵌套
- 2、循环语句
- 2.1、while循环
- 2.1.1、基本格式
- 2.1.2、while语句中的break和continue
- 2.1.3、getchar与缓冲区
- 2.2、for循环
- 2.2.1、基本格式
- 2.2.2、for循环中的break和continue
- 2.2.3、for循环中,关于循环控制变量
- 2.2.4、一些特殊的for循环演示
- 2.3、do...while()循环
- 2.3.1、基本格式
- 2.3.2、do...while()循环中的break和continue
- 3、相关练习
- 3.1、计算 n的阶乘
- 3.2、计算 1!+2!+3!+……+10!
- 3.3、在一个有序数组中查找具体的某个数字n。(二分查找/折半查找学习)
- 3.4、编写代码,演示多个字符从两端移动,向中间汇聚(Sleep、system("cls"))
- 3.5、编写代码实现,模拟用户登录情景,并且只能登录三次。
- 4、猜数字游戏
- 4.1、基本逻辑框架搭建
- 4.2、game内部完善:
- 4.2.1、随机数rand(srand、time)
- 4.2.2、猜数字
- 4.3、整个代码
- 5、goto
- 5.1、基本介绍
- 5.2、关机程序
C语言是结构化的程序设计语言,有顺序结构、选择结构、循环结构。
1、分支语句
1.1、if语句
1.1.1、基本格式
1)、基本格式与演示
if(表达式)
语句1;
else
语句2;
//多分支
if(表达式1)
语句1;
else if(表达式2)
语句2;
else
语句3;
演示实例:
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if (age < 18)
{
printf("少年\n");
}
else if (age >= 18 && age < 30)
{
printf("青年\n");
}
else if (age >= 30 && age < 50)
{
printf("中年\n");
}
else if (age >= 50 && age < 80)
{
printf("老年\n");
}
else
{
printf("老寿星\n");
}
}
if语句中一种错误的写法:if (18 <= age < 30)
(age >= 18 && age < 30)
,这种多重逻辑关系,先判断age >= 18
的逻辑真假,再判断age < 30
的逻辑。
关系运算符中有一个短路问题,其将在后续的操作符详解中讲述。
1.1.2、逻辑真假与悬空else
2)、在C语言中如何表示真假?
C语言中,0表示假,非0表示真。
3)、悬空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的匹配。else是和它离的最近的if匹配的。
这里提醒了我们需要有一定的代码风格。
演示实例一:
int num = 1;
if(num == 5)
{
printf("hehe\n");
}
int num = 1;
if(5 == num)//这种写法可以防止判等写为赋值
{
printf("hehe\n");
}
演示实例二:
if (condition) {
return x;
}
return y;
if(condition)//相对的逻辑表达清晰
{
return x;
}
else
{
return y;
}
1.1.3、练习
1)、判断一个数是否为奇数
int main()
{
int n = 0;
scanf("%d", &n);
if (n % 2 == 1)
{
printf("奇数\n");
}
return 0;
}
2)、输出1-100之间的奇数
int main()
{
for (int i = 1; i < 100; ++i)
{
if (i % 2)
{
printf("%d ", i);
}
}
return 0;
}
int main()
{
for (int i = 1; i < 100; i += 2)
{
printf("%d ", i);
}
return 0;
}
1.2、switch语句
1.2.1、基本格式:break、case、default
1)、基本格式与演示
switch(整型表达式)
{
语句项;
}
实例演示一:
#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("error\n");
break;
}
}
两个注意事项:
Ⅰ、switch语句中没办法直接实现分支,搭配break使用才能实现真正的分支。break
语句的实际效果是把语句列表划分为不同的分支部分。
Ⅱ、当 switch 表达式的值并不匹配所有 case 标签的值时, default
子句后面的语句就会执行。每个switch语句中只能出现一条default子句。
Ⅲ、switch(整型表达式)
,case 整型常量表达式 :
需要注意此处放置的必须是整型表达式。
1.2.2、练习:switch语句嵌套
switch语句的嵌套举例:switch中,break只能跳出一层循环。
以下代码输出结果是什么?
#include <stdio.h>
int main()
{
int n = 1;
int m = 2;
switch (n)
{
case 1:
m++;
case 2:
n++;
case 3:
switch (n)
{//switch允许嵌套使用
case 1:
n++;
case 2:
m++;
n++;
break;
}
case 4:
m++;
break;
default:
break;
}
printf("m = %d, n = %d\n", m, n);
return 0;
}
结果:5 ,3
2、循环语句
2.1、while循环
2.1.1、基本格式
1)、基本格式与演示
while(表达式)
循环语句;
演示:在屏幕上打印1-10的数字
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
printf("%d ", i);
i = i + 1;
}
return 0;
}
2.1.2、while语句中的break和continue
1)、while语句中的break
while中的break
用于永久终止循环。即,在循环中遇到break,就停止后期的所有的循环。
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
if (i == 5)
break;
printf("%d ", i);
i = i + 1;
}
return 0;
}
2)、while语句中的continue
continue
是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分。进行下一次循环的入口判断。
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
if (i == 5)
continue;
printf("%d ", i);
i = i + 1;
}
return 0;
}
演示代码中,输出结果为陷入死循环,这是因为上述代码i == 5
满足if语句,遇到continue
,于是本次循环后续部分不执行,直接跳到下一次循环,但此时i仍旧是5,所以仍旧满足if语句,故陷入了死循环。
2.1.3、getchar与缓冲区
1)、getchar的基本使用方法介绍
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
}
Ctrl+Z
:能够让getchar、scanf
返回一个EOF
,从而达到结束循环的目的。
2)、getchar的实际使用列举:清理缓冲区
演示代码如下:
输入一串字符串表示密码,并确认是否输入成功:
int main()
{
char password[20] = { 0 };
printf("请输入密码:");
scanf("%s", password);
printf("请确认密码(Y/N):");
int ch = getchar();
if ('Y' == ch)
{
printf("确认成功\n");
}
else {
printf("确认失败\n");
}
return 0;
}
现象如下:
输入密码123456,我们本想再次输入Y,以表示密码确认,结果却是当我们输入完123456后,直接显示确认失败。
这是为什么呢?
scanf、getchar从键盘中读取数据时,中间有一个缓冲区,数据先从键盘存储在缓冲区,再被scanf、getchar读取到。
我们输入上述密码串123456后,为了执行下一语句还按了回车键,即\n,实际缓冲区中存储的是123456\n。
scanf将前面的数字读取走,缓冲区中还留下\n,那么当下一条语句getchar到缓冲区中拿去数据时,读到的就是\n。
最后结果为确认失败。
有没有什么解决方法:
有人可能想到,我们在确认之前再放置一个getchar,以读到\n。
int main()
{
char password[20] = { 0 };
printf("请输入密码:");
scanf("%s", password);
getchar();//放置用于清理的getchar
printf("请确认密码(Y/N):");
int ch = getchar();
if ('Y' == ch)
{
printf("确认成功\n");
}
else {
printf("确认失败\n");
}
return 0;
}
但是这种方法还不够完善,因为我们可能遇到在正确密码后输入一串字符的情况,那么一个getchar是解决不了问题的。
故有了如下情况:
int main()
{
char password[20] = { 0 };
printf("请输入密码:");
scanf("%s", password);
while(getchar()!='\n')
{ }
printf("请确认密码(Y/N):");
int ch = getchar();
if ('Y' == ch)
{
printf("确认成功\n");
}
else {
printf("确认失败\n");
}
return 0;
}
当然,上述那段代码也可以写为:
int tmp = 0;
while ((tmp=getchar()) != '\n')
{
}
3)、getchar的其它使用场景演示
比如下面这一串,其作用是:只打印数字字符,跳过其他字符。
#include <stdio.h>
int main()
{
char ch = '\0';
while ((ch = getchar()) != EOF)
{
if (ch < ‘0’ || ch > ‘9’)
continue;
putchar(ch);
}
return 0;
}
2.2、for循环
2.2.1、基本格式
1)、基本格式与演示
for(表达式1; 表达式2; 表达式3)
循环语句;
表达式1:初始化部分,用于初始化循环变量的。
表达式2:条件判断部分,用于判断循环时候终止。
表达式3:调整部分,用于循环条件的调整。
使用for循环在屏幕上打印1-10的数字:
#include <stdio.h>
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
printf("%d ", i);
}
return 0;
}
2.2.2、for循环中的break和continue
1)、for循环中的break
for循环中的break和while循环基本大差不差。
#include <stdio.h>
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
break;
printf("%d ", i);
}
return 0;
}
1)、for循环中的continue
for循环中的continue作用也一样,只是由于for循环中调整部分(表达式3)在当前循环结束,下一次循环之前起效,所以其输出结果不同于while。
#include <stdio.h>
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
continue;
printf("%d ", i);
}
return 0;
}
上述代码稍微做一点改变,就能和while循环得到结果一致。
#include <stdio.h>
int main()
{
int i = 0;
for (i = 1; i <= 10; )
{
if (i == 5)
continue;
printf("%d ", i);
i++;
}
return 0;
}
2.2.3、for循环中,关于循环控制变量
一般情况下,建议遵守以下规则:
1. 不可在for 循环体内修改循环变量,防止 for 循环失去控制。
2. 建议for语句的循环控制变量的取值采用“前闭后开区间”写法。
int i = 0;
for(i=0; i<10; i++)//前闭后开
{}
for(i=0; i<=9; i++)//两边都是闭区间
{}
2.2.4、一些特殊的for循环演示
1)、演示一
for循环中的初始化部分,判断部分,调整部分是可以省略的。
省略条件判等部分,则恒为真,会陷入死循环。
for (;;)
{
printf("hehe\n");
}
2)、演示二
for循环嵌套,以下代码打印多少个hehe
?
int i = 0;
int j = 0;
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10; j++)
{
printf("hehe\n");
}
}
回答:外10,内10,共100次。
那么,将其改为如下情况,省略掉初始化部分,这里打印多少个hehe
?
int i = 0;
int j = 0;
for (; i < 10; i++)
{
for (; j < 10; j++)
{
printf("hehe\n");
}
}
回答:共10次,首次i=0时,j从0自增到10,共10次打印,随后i从1到10的过程,j不满足条件。
3)、演示三
多重变量用于控制for循环。
int x, y;
for (x = 0, y = 0; x < 2 && y < 5; ++x, y++)
{
printf("hehe\n");
}
return 0
4)、演示四
以下代码循环多少次?
#include <stdio.h>
int main()
{
int i = 0;
int k = 0;
for (i = 0, k = 0; k = 0; i++, k++)
k++;
return 0;
}
结果:0次,k =0
为赋值,表达式为假,不进入循环。
2.3、do…while()循环
2.3.1、基本格式
1)、基本格式与演示
do
循环语句;
while(表达式);
do...while()
语句,循环至少执行一次:
#include <stdio.h>
int main()
{
int i = 10;
do
{
printf("%d\n", i);
} while (i < 10);
return 0;
}
2.3.2、do…while()循环中的break和continue
1)、do…while()循环中的break
#include <stdio.h>
int main()
{
int i = 1;
do
{
if (5 == i)
break;
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
2)、do…while()循环中的continue
#include <stdio.h>
int main()
{
int i = 1;
do
{
if (5 == i)
continue;
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
3、相关练习
3.1、计算 n的阶乘
int main()
{
int n = 0;
scanf("%d", &n);
int sum = 1;
for (; n > 0; --n)
{
sum *= n;
}
printf("%d\n", sum);
}
上述代码其实存在缺陷,一个是多组输入,一个是int类型有上限值。
#include<limits.h>
3.2、计算 1!+2!+3!+……+10!
int main()
{
int n = 0;
scanf("%d", &n);
int ret = 0;
for (int i = 1; i <= n; ++i)
{
int sum = 1;
for (int j = i; j > 0; --j)
{
sum *= j;
}
ret += sum;
}
printf("%d\n", ret);
return 0;
}
此处做了些修改,计算1~n的阶乘之和。
上述的一种简化写法,不必每次都从1开始重新往后累乘,1
、1*2
、1*2*3
、……;可以每次都在前一次的基础上乘上一个数即可。
int main()
{
int n = 0;
scanf("%d", &n);
int ret = 0;
int sum = 1;
for (int i = 1; i <= n; ++i)
{
sum *= i;
ret += sum;
}
printf("%d\n", ret);
return 0;
}
3.3、在一个有序数组中查找具体的某个数字n。(二分查找/折半查找学习)
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };//有序数组
int sz = sizeof(arr) / sizeof(arr[0]);//大小
int left = 0;
int right = sz-1;//下标
int key = 6;//我们要找的目标元素
while (left <= right)//left==right:该情况有可能为我们要寻找的目标元素
{
int mid = (left + right) / 2;//中间元素下标
if (arr[mid] < key)
{
left = mid + 1;
}
else if (arr[mid] > key)
{
right = mid - 1;
}
else//arr[mid] ==key
{
printf("找到了,下标是%d\n",mid);
break;//跳出循环
}
}
if (left > right)//需要加该条件是因为当循环结束,下述代码会执行,故需要一个约束条件
{
printf("找不到\n");
}
return 0;
}
注意事项:
1、这种查找方法适用于有序数组;
2、注意left、right
的含义,是下标还是元素,之后学习指针后也要融汇进入;
3、注意循环中的条件(是否要加等于left <= right
),和循环结束的break
跳出,以及找不到的约束条件left > right
。
4、上述写法中,int mid = (left + right) / 2;
这种直接计算的方式,会受到类型大小限制,此处还有一种写法,即:
int mid = left + (right-left) / 2;
3.4、编写代码,演示多个字符从两端移动,向中间汇聚(Sleep、system(“cls”))
要实现的效果如下图:
//welcome to the world.
//#####################
//w###################.
//we#################d.
//wel###############ld.
//welc#############rld.
//……
版本演示1.0:
//welcome to the world.
#include<string.h>
int main()
{
char arr1[] = "welcome to the world.";
char arr2[] = "#####################";
int left = 0;
int right = strlen(arr1) - 1;
while (left <= right)
{
printf("%s\n", arr2);
arr2[left] = arr1[left];
arr2[right] = arr1[right];
left++;
right--;
}
printf("%s\n", arr2);
return 0;
}
版本演示2.0:
//welcome to the world.
#include<string.h>
#include<Windows.h>
#include<stdlib.h>
int main()
{
char arr1[] = "welcome to the world.";
char arr2[] = "#####################";
int left = 0;
int right = strlen(arr1) - 1;
while (left <= right)
{
printf("%s\n", arr2);
Sleep(1000);//单位:毫秒,注意大写
system("cls");//清屏作用
arr2[left] = arr1[left];
arr2[right] = arr1[right];
left++;
right--;
}
printf("%s\n", arr2);
return 0;
}
3.5、编写代码实现,模拟用户登录情景,并且只能登录三次。
要求:只允许输入三次密码,如果密码正确则提示登录成,如果三次均输入错误,则退出程序。
注意事项:
1、此处字符串的比较不能使用判等运算符:if ( password == KEY)
,而是需要使用库函数strcmp、strncmp
#include<string.h>
#define KEY "123456"
int main()
{
//假设正确密码为:“123456”
char password[20] = { 0 };
int i = 0;
for (i = 0; i < 3; i++)
{
scanf("%s", password);
if (strcmp(password, KEY) == 0)
{
printf("密码正确\n");
break;//break跳出的是for循环
}
else
{
printf("密码错误,重新输入\n");
}
}
if (3 == i)
{
printf("三次错误,结束程序\n");
}
return 0;
}
4、猜数字游戏
游戏逻辑: 电脑随机生成一定范围内的数字,对玩家猜测的数字进行判断,并对其判断结果做出相应反馈。(比如:猜小了,猜大了,猜对了。)
4.1、基本逻辑框架搭建
#include<stdio.h>
void Menu()
{
printf("####################################\n");
printf("############ 1.play ################\n");
printf("############ 0.exit ################\n");
printf("####################################\n");
}
void Game()
{
//随机数生成
//猜数字
}
int main()
{
int input = 0;
do {
Menu();
printf("请选择:");
scanf("%d", &input);
switch(input)
{
case 1:
Game();
break;
case 0:
printf("游戏结束,退出\n");
break;
default:
printf("输入错误,重新输入\n");
break;
}
} while (input);
return 0;
}
4.2、game内部完善:
4.2.1、随机数rand(srand、time)
1)、阶段讲解一
关于随机数的生成,我们需要使用一个名为rand
的函数,相关介绍如上。
需要注意的是,rand生成的随机数依托于srand,每次关闭程序重新打开,生成的随机数值一致,不符合任意的要求。
2)、阶段讲解二
由上图,srand中传入不同值,我们才能在rand中得到不同的值。故相对理想的状态是,传入一直在发生变化的随机值,因此借用时间戳。
srand((unsigned int)time(NULL));//强制类型转换
我们在game函数中写下这段代码,就可以为srand获取到随机值,但这里存在一个问题,即当我们输入太快时,频繁调用game函数,因间隙很短所获取的随机值会相同。
考虑到一次游戏只需要生成一次随机值,因此我们将该代码放到main函数中。这样生成的随机值就不会出现短时相同的情况。
3)、阶段讲解三
此外还有一个需要注意的问题,查看RAND_MAX可知,这个随机数的范围是很大的, 如果我们不做一些处理,那么一轮游戏要猜出正确值来会相对耗时,降低游戏可玩性。
因此我们需要对所生成的随机值做范围处理:
int ret = rand()%100+1;//1~100
4.2.2、猜数字
int guss = 0;
while (1)
{
printf("请输入猜测数字:");
scanf("%d", &guss);
if (guss < ret)
{
printf("猜小了\n");
}
else if (guss > ret)
{
printf("猜大了\n");
}
else
{
printf("猜对了\n");
break;
}
}
4.3、整个代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void Menu()
{
printf("####################################\n");
printf("############ 1.play ################\n");
printf("############ 0.exit ################\n");
printf("####################################\n");
}
void Game()
{
//随机数生成
int ret = rand()%100+1;//1~100
//猜数字
int guss = 0;
while (1)
{
printf("请输入猜测数字:");
scanf("%d", &guss);
if (guss < ret)
{
printf("猜小了\n");
}
else if (guss > ret)
{
printf("猜大了\n");
}
else
{
printf("猜对了\n");
break;
}
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do {
Menu();
printf("请选择:");
scanf("%d", &input);
switch(input)
{
case 1:
Game();
break;
case 0:
printf("游戏结束,退出\n");
break;
default:
printf("输入错误,重新输入\n");
break;
}
} while (input);
return 0;
}
5、goto
5.1、基本介绍
多层循环嵌套下,使用break
是达不到一次跳出两层或多层循环的目的,它只能从最内层循环退出到上一层的循环。而goto
语句却能做到。其最常见的用法就是终止程序在某些深度嵌套的结构处理中。
例如:C语言中提供的可以随意滥用的 goto语句和标记跳转的标号。
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
…
error:
if(disaster)
// 处理错误情况
需要注意的是,在一个函数中使用goto语句,其可以在函数内往前或往后跳跃,但做不到跨函数跳跃。
5.2、关机程序
Windows下的关机程序相关指令:
shutdown -s:关机
shutdown -s -t:设置关机时间t
shutdown -a:取消关机
使用goto的情况下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define KEY "天生我材必有用"
int main(void)
{
char input[100] = { 0 };
system("shutdown -s -t 60");
tryagain:
printf("请注意,本机将于60s后自动关机,若想取消,请输入:%s。\n", KEY);
scanf("%s", input);
if (strcmp(input,KEY) == 0)
{
printf("乖孩子\n");//验证
system("shutdown -a");
}
else
{
goto tryagain;
}
return 0;
}
使用while的情况下:
#define KEY "天生我材必有用"
int main(void)
{
char input[100] = { 0 };
system("shutdown -s -t 60");
printf("请注意,本机将于60s后自动关机,若想取消,请输入:%s。\n", KEY);
scanf("%s", input);
while (1)
{
if (strcmp(input, KEY) == 0)
{
printf("乖孩子\n");//验证
system("shutdown -a");
break;
}
}
return 0;
}