【手把手带你练习】分支和循环语句
前言
上一篇文章我们介绍了分支和循环语句,但是光说不练假把式,下面我们就通过以下练习来对上一章的内容进行巩固吧!
分支语句练习
1. 判断一个数是否为奇数
解题思路:
整数分为奇偶数,偶数能被 2 整除,奇数不能被 2 整除,所以如果一个数除以 2 余 0,则该数是偶数,否则是奇数。
具体做法:
先输入一个数用 i 保存起来,再用 if 语句进行判断。
代码如下:
#include<stdio.h>
int main()
{
int i = 0;
scanf("%d", &i);//先输入一个数,放到i中
if (i % 2 == 0)//if 语句进行判断
{
printf("%d 不是奇数",i);
}
else
{
printf("%d 是奇数",i);
}
return 0;
}
输入5,输出结果如下:
2. 输出1-100之间的奇数
解题思路:
法一:把1~100的数一一列举,再依次判断是否为奇数,是奇数的输出,否则不输出。
具体做法:
先用一个循环把1~100的数列出来,再在循环中进行是否为奇数的判断,是则输出,否则不输出。
代码如下:
#include<stdio.h>
int main()
{
int i = 1;
while (i <= 100)//列出1-100
{
if (i % 2 != 0)//判断是否为奇数
printf("%d ", i);//是则输出
i++;
}
return 0;
}
输出结果如下:
法二:因为连续奇数之间的差值为 2,而我们可以很容易的知道第一个奇数是1,所以只要依次加上差值 2,就可以写出1~100之间的所有奇数
具体做法:
先输出1,再依次+2输出,知道 i >100为止
#include<stdio.h>
int main()
{
int i = 1;
while (i <= 100)
{
printf("%d ", i);//从1开始输出
i += 2;//每次+2再输出
}
return 0;
}
输出结果如下:
循环语句练习
3. 计算 n 的阶乘。
解题思路:
n!=1 * 2 * 3 * …… * (n-1)* n
具体做法:
先定义一个整形 z=1,再依次把1~n乘以 z 。
代码如下:
#include<stdio.h>
int main()
{
int i = 1;
int z = 1;//用来储存乘积的值
int n = 0;
scanf("%d", &n);
for (i = 1; i <= n; i++)//依次列出1-n的值
{
z *= i;//进行累乘
}
printf("n! = %d", z);
return 0;
}
}
输入4,输出结果如下:
4. 计算 1!+2!+3!+……+10!
解题思路:
第三题中我们已经求出了n!,现在要把 1~10的阶乘加起来,所以我们应该用到两个循环:
一个用来求阶乘,一个用来依次把阶乘加起来。
具体做法:
先定义一个整形 z=1,再依次把1~n乘以 z 。
代码如下:
#include<stdio.h>
int main()
{
int i = 1, j = 1, z = 1;
int sum = 0;
for (i = 1; i <= 10; i++)//列出1-10
{
for (j = 1; j <= i; j++)
{
z *= j;//计算阶乘
}
sum += z;//把计算的阶乘加起来
}
printf("sum = %d", sum);
return 0;
}
输出结果如下:
但是,我们怎么知道这个结果是不是正确的呢?
计算1~10的阶乘的和有点太大, 我们可以先用1-3的阶乘的和来验证一下。
1!+2!+3!=9。结果应输出9,而上图结果却输出了15,说明一定是哪里出了bug,这时候应该怎么办呢?
遇到问题先别慌,给up主点个赞放松一下,咱们再一步步来找bug!
现在我们的程序可以正常运行,说明我们是运行的时候出了错误,那么我们可以用调试的方法让代码一步一步执行,观察代码是不是按照我们的期望执行的,当我们找到代码执行与期望不符的地方,说明bug就在那里。
按住 F10 或者Ctrl + F10进入调试。
F10 - 单步执行(逐过程)
F11 - 单步执行(逐语句) - 遇到自定义函数,想进入函数时按F11 用于看函数内部细节时
程序进入调试后,打开一个监视窗口
继续按F10观察程序的运行。
我们继续往下调试。
调试继续进行。
思考一下,为什么 z 变成了12?
我们求3!的时候,z 应该是 1 * 2 * 3 = 6。
而监视器中却是12,说明在 z * 1 * 2 * 3 之前,z 的值不是 1 ,而是 2 。那么为什么 z 的值是 2 呢?
因为 z 的定义放在了for循环的外面,每一次我们求完 z 的值之后,z 并没有被重置,而是把值累积了起来,于是求出的值与理想中的不一致。
所以,我们发现,是由于每次求完阶乘之后,z 的值没有重新赋为 1,导致了错误,所以我们应该在内层的 for 循环前把 z 重新赋为 1 ,再运行结果。
这时候我们再把原来改为 3 的值改回 10。
#include<stdio.h>
int main()
{
int i = 1, j = 1, z = 1;
int sum = 0;
for (i = 1; i <= 10; i++)
{
z = 1;
for (j = 1; j <= i; j++)
{
z *= j;//计算阶乘
}
sum += z;//把计算的阶乘加起来
}
printf("sum = %d", sum);
return 0;
}
再次运行程序,结果如下:
但是大家有没有发现,这个方法其实有点繁琐。我们每求一个阶乘都要从 1 开始往后乘。而实际上,通过仔细观察我们发现:
1!=1
2!=1 * 2 =1!*2
3!=1 * 2 * 3 = 2!*3
4!=1 * 2 * 3 * 4=3!*4
5!=……………=4!*5
6!=……………=5!*6
7!=……………=6!*7
……
n!=……=(n-1)!*n
那么我们只需要在前面求出来的 (n-1)!的基础上 *n就行了。
代码简化如下:
#include<stdio.h>
int main()
{
int i = 1, z = 1;
int sum = 0;
for (i = 1; i <= 10; i++)
{
z *= i;//计算阶乘 - n!=(n-1)!*n
sum += z;//把计算的阶乘加起来
}
printf("sum = %d", sum);
return 0;
}
运行结果和上面一样:
通过上面的问题,我们应该学会如何运用调试来找bug。
如果你觉得以上方法对你有用,记得动动手指点赞收藏关注一波噢
综合练习
5. 在一个有序数组中查找具体的某个数字n。
问题描述:
编写一段代码,在一个有序数组arr[ ]={1,2,3,4,5,6,7,8,9,10},中查找具体的某个数字,数字为7。
思路:
先编写出一个有数组,然后依次把我们要找的数字与数组中的每一个元素进行比较,找到则输出数组下标,找不到则输出“找不到”。
具体做法:
- 先把要有序数组、要找的具体数字和数组下标存起来。
- 计算数组元素个数。
- 将每个元素依次与要找的数进行比较
- 找到了则打印数组下标,跳出循环;如果没找到,则打印“找不到”
具体代码如下:
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//定义一个有序数组
int k = 7;//定义我们要找的具体数字
int i = 0;//定义下标
int sz = sizeof(arr) / sizeof(arr[0]);//计算数组元素个数
for (i = 0; i < sz; i++)
{
if (arr[i] == k)//每个元素依次与k进行比较
{
printf("找到了,下标为%d\n", i);
break;//找到之后,跳出循环;没找到则继续找直到i>=k
}
}
if (i >= sz)//当i>=k,说明没有找到
{
printf("找不到");
}
return 0;
}
程序运行结果如下:
k 改为17时,运行结果如下:
但是我们发现这是一个有序的数组。
如果我们要找的数在最后一个,那程序就要找很多次,有没有办法可以对代码进行优化呢?
答案是有的。既然这是一个有序数组,那么我们可以思考从中间查找,就像玩猜数字一样,我们先猜一个数字,然后对方告诉你猜大了还是猜小了,如果猜大了,我们就往小了猜,如果猜小了,我们下次就往大了猜,这样比从1开始依次往大猜效率会更高。
当我们每次猜都从中间值猜起,那么我们每次缩小的范围就更多,在理论上效率更高。
这也就是我们接下来要实现的折半查找(二分查找)。
那么我们应该如何实现呢?
实现代码如下:
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 17;
int sz = sizeof(arr) / sizeof(arr[0]);//计算数组元素个数
int left = 0;//最左边元素下标
int right = sz - 1;//最右边元素下标
while (left<=right)
{
int mid = (left + right) / 2;//求中间元素下标
if (arr[mid] > k)//每个元素依次与k进行比较
{
right = mid - 1;
}
else if (arr[mid] < k)
{
left = mid + 1;
}
else
{
printf("找到了,下标为%d\n", mid);
break;//找到之后,跳出循环;
}
}
if (left>right)//当left>right,说明没有找到
{
printf("找不到");
}
return 0;
}
程序运行结果如下:
k 改为17时,运行结果如下:
虽然这段代码看起来比第一种方法更长,但是当要查找的数组很大时,程序查找的速度就会快很多。
6. 从两边开始逐渐打印字符串。
问题描述:
编写一段代码,使“Hello world!”字符串从两边开始逐渐向中间打印,直到打印出整个字符串为止。
即:屏幕上如下逐渐打印出“Hello world!”
##########
H########!
He######d!
Hel#####ld!
Hell####rld!
Hello##orld!
Hello world!
解题思路:
字符串从“##########”变成“Hello world!”,首先应该分别用arr1和arr2两个字符类型数组来保存,然后再依次把arr2中两端的值赋给arr1中两端的值,每赋值依次,打印依次,依次来实现逐渐打印。
具体做法:
- 首先分别定义两个字符串,用strlen函数求出字符串长度,然后写出左右两端元素的下标
- 分别将arr2中两端元素的值传给arr1,然后打印arr1。
- 因为我们要逐渐打印字符串,所以每次打印之后要引用一个系统函数Sleep( )让程序暂停一下,再继续进行。
- 重复2、3步骤直到整个字符串打印出来为止。
求字符串长度的函数:strlen( )
当然,我们也可以用sizeof来实现:
len = sizeof (arr1) / sizeof (arr1[0])
让程序停顿的函数:Sleep(1000)
实现代码如下:
#include<stdio.h>
#include<string.h>
#include<windows.h>
int main()
{
char arr1[] = "############";//分别定义两个字符串
char arr2[] = "Hello world!";
int len = strlen(arr1);//求字符串长度
//strlen - 用于求字符串长度的函数,使用时要引头文件<string.h>
int left = 0;//分别定义两端的下标
int right = len - 1;
while (right >= left)//循环直到所有元素都打印出来为止
{
arr1[left] = arr2[left];//将arr2中的左边元素传递给arr1
arr1[right] = arr2[right];//将arr2中的右边元素传递给arr1
printf("%s\n", arr1);//打印arr1
Sleep(1000);//停顿1000毫秒 - 1秒
left++;//调整两端坐标
right--;
}
return 0;
}
程序运行结果如下:
那么我们有没有办法让所有打印都在一行实现呢?
这时候我们就要借助清空函数的帮忙咯~我们在下一次打印之前,把之前在屏幕上打印的内容都清空,那么每次打印的时候,文字就都出现在第一行啦!
改变后结果如下:
7. 模拟用户登录情景
问题描述:
编写一段代码,模拟用户输入密码,并且只允许输入三次密码,如果密码正确则提示登录成功,如果三次均输入错误,则退出程序。密码为:abcdef
思路:先把密码存到字符串中,然后再把输入的密码与字符串比较,如果一样,则输出登录成功,程序结束;如果不同则重新输入,输入和比较最多循环三次,三次之后程序结束。
具体做法:
- 首先定义一个字符串用来存放密码。
- 在屏幕上输出“请输入密码:>”提示用户输入密码
- 开始一个循环,把用户输入的密码存到1.中定义的字符串
- 将字符串与正确密码进行比较,密码正确则输出“登录成功”,跳出循环;密码错误,则输出“密码错误,请重新输入:>”提示用户重新输入密码。
- 用 if 语句实现输入超过三次都不正确时,输出“很抱歉,三次密码均错误,您已没有机会”
这里要强调的是,字符串的比较不能直接将两个字符串用“ == ”来判断相等,因为当我们拿password与密码“ abcdef ”进行比较时,passowrd传过去的是password的地址,而“ abcdef ”传的是首元素的地址,我们比较的是实际上是地址而不是字符串的内容。
所以这里在比较字符串的时候,我们要用到字符串函数:strcmp。
详见https://cplusplus.com/reference/cstring/strcmp/?kw=strcmp
代码如下:
#include<stdio.h>
#include<string.h>
int main()
{
int i = 0;
char password[20] = { 0 };//定义一个字符串来存放密码
for (i = 0; i < 3; i++)//循环三次,只能输入三次密码
{
printf("请输入密码:>");
scanf("%s", password);
if (strcmp(password, "abcdef") == 0)//对比输入的字符串和密码
{
printf("登录成功\n");
break;//密码正确,则跳出循环
}
else
{
printf("密码错误\n");
}
}
if (i >= 3)
{
printf("很抱歉,三次密码均错误,您已没有机会\n");
}
return 0;
}
运行结果如下:
总结
经过上面几道题目的练习,相信你一定对C语言中的分支和循环语句有了更深的认识和理解,我们会发现,灵活地运用分支和循环语句,我们就已经可以解决很多问题了。
最后,本文的代码已上传至Gitee啦!想要的话点击链接自取噢~https://gitee.com/fang-qiuhui/my-code/blob/master/practice_2021_6_19/practice_2021_6_19/practice_2021_6_19.cpp
感谢你看到这里,和我一起努力学习,持续精进,欢迎大家持续关注我的文章噢!
如果你喜欢我的文章,或者觉得本文给你带来了收获,别忘了动动小手指一键三连噢!