Hello,大家好!!!这里是小周为您带来的呕心沥血之作------C语言秘籍!!
C语言秘籍分为初阶和高阶两部!!跟着小周学定会让你C语言功力大成,称霸武林,话不多说,我们接着上回继续开讲!!!!!
二、分支语句和循环语句
从今天开始我将带领大家深入学习每一个知识点
分支语句
-
if
-
switch
循环语句
-
while
-
for
-
do while
goto语句
1、什么是语句?
在C语言中,语句可分为以下五类:
-
表达式语句
-
函数调用语句
-
控制语句
-
复合语句
-
空语句
int main() { 3 + 5;//表达式语句 printf("hehe");//函数调用语句 int i = 1; for (i = 1; i < 100; i++) { //复合语句,放在{ }中的这些语句 printf("%d ", i); printf("%d ", i+1); printf("%d ", i+2); } ;//空语句 return 0; }
本章后面介绍的是控制语句。
控制语句用于控制程序的执行流程,以实现程序的各种结构方式
它们由特定的语句定义符组成,C语言有九种控制语句。
可分成以下三类:
-
条件判断语句也叫分支语句:if语句、switch语句;
-
循环执行语句:do while语句、while语句、for语句;
-
转向语句:break语句、goto语句、continue语句、return语句。
2、分支语句(选择结构)
漫漫人生路,我们总是时时刻刻在做选择!!
如果你好好学习,校招时拿一个好offer,走上人生巅峰,迎娶白富美
如果你不学习,毕业等于失业,回家种地
2.1 if语句
2.1.1 if语句的语法结构
//1、
if(表达式)
语句;
//2、
if(表达式)
语句1;
else
语句2;
//3、
if(表达式)
语句1;
else if(表达式)
语句2;
else
语句3;
2.1.2 代码讲解
情景再现:小于18岁请在屏幕上打印未成年,大家想一想看看代码怎么写
公布答案:
int main()
{
int age = 0;
scanf("%d", &age);
if (age < 18)
{
printf("未成年\n");
}
}
情景再现:小于18岁请在屏幕上打印未成年,否则就打印成年,大家再想一想看看代码怎么写
公布答案:
int main()
{
int age = 0;
scanf("%d", &age);
if (age < 18)
{
printf("未成年\n");
}
else
{
printf("成年\n");
}
}
我们规定18岁以下是少年,18-30岁是青年,30-50岁是中年,50-80岁是老年,80岁以上是老寿星大家,再想一想看看代码怎么写
公布答案:
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 (age < 18)
printf("青少年\n");
else
if (age >= 18 && age <= 30)
printf("青年\n");
else if (age >= 31 && age <= 50)
printf("中年\n");
else if (age >= 51 && age <= 80)
printf("中老年\n");
else if (age >= 81 && age <= 100)
printf("老年\n");
else
printf("老寿星\n");
在这里再带大家回忆一下,如果表达式的结果为真,则语句执行。在C语言中如何表示真假?
0表示假,非0表示真。
那我们说日常工作的场景肯定不能像例子一样简单,肯定有多个语句构成,来实现对应的功能
如果条件成立,要执行多条语句,我们应该使用代码块
#include <stdio.h>
int main()
{
if (表达式)
{
语句列表1;
}
else
{
语句列表2;
}
return 0;
}
这里的一对 { } 就是一个代码块
2.1.3 悬空else
这里考大家一道,看看各位能不能发现其中的奥妙
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匹配的,结果应该是打印haha,其实不是这样的
else的匹配:else是和它离的最近的if匹配的。
我们现在的编译器比较智能,比较人性化,当代码粘贴到编译器时,会自动匹配,所以很容易就发现输出什么
如果想实现打印haha的结果不妨这么做
#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.1.4 if书写形式的对比
我们先来看这两组代码
//代码1
if (condition) {
return x;
}
return y;
//代码2
if(condition)
{
return x;
}
else
{
return y;
}
其实这两组代码效果是一样的,但是代码2的逻辑更加清晰,不容易出错
接着看这两组
//代码3
int num = 1;
if(num == 5)
{
printf("hehe\n");
}
//代码4
int num = 1;
if(5 == num)
{
printf("hehe\n");
}
差别就在判断条件上,那小周个人习惯是代码3的方式,但其实代码四的逻辑更加清晰,不容易出错,那为什么呢?我们发现在写代码时,难免会出现各种各样的错误
因为我们少写了一个=,就造成了这种结果,程序死循环了,如果我们按照代码4来写的话,就可以及时发现这种错误
我们就能及时发先错误并改正
2.1.5 练习
这里有两道小题拿来给大家练练手,大家先自己下去尝试写一写,看看能不能实现对应的功能,有不明白的问题可以私信我啊
1、判断一个数是否是奇数
int main()
{
int n = 0;
//输入
scanf("%d", &n);
if (n % 2 == 1)
{
printf("是奇数\n");
}
else
{
printf("不是奇数\n");
}
return 0;
}
2、输出1-100之间的奇数
//方法1
int main()
{
int i = 1;
while (i <= 100)
{
//判断i是奇数的话,就打印i
if(i % 2 == 1)//%2等于1的话就是奇数
printf("%d ", i);
++i;
}
return 0;
}
//方法2
//因为奇数是1 3 5 7 9......所以每次在原有基础上+2,就省去了方法一的判断和循环
int main()
{
int i = 1;
while (i <= 100)
{
printf("%d ", i);
i = i + 2;
}
return 0;
}
2.2 switch语句
switch语句也是一种分支语句,常常用于多分支的情况
比如:
输入1,输出星期一
输入2,输出星期二
输入3,输出星期三
输入4,输出星期四
输入5,输出星期五
输入6,输出星期六
输入7,输出星期日
int main()
{
int day = 0;
//输入
scanf("%d", &day);
if (1 == day)
printf("星期1\n");
else if (2 == day)
printf("星期2\n");
else if (3 == day)
printf("星期3\n");
else if (4 == day)
printf("星期4\n");
else if (5 == day)
printf("星期5\n");
else if (6 == day)
printf("星期6\n");
else if (7 == day)
printf("星期天\n");
else
printf("输入错误\n");
return 0;
}
我们发现上述代码有点麻烦,那我们不想用if else结构,那我们就得有不一样的语法形式,这就是switch 语句。
switch(整型表达式)
{
语句项;
}
而语句项是什么呢?
//是一些case语句:
//如下:
case 整形常量表达式:
语句;
我们发现,switch语句有一些特殊的规定,switch后面的()中必须是整型表达式,case后面也得接整形常量表达式
那介绍完switch的用法,这个代码该怎么写呢?
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
printf("星期一\n");
case 2:
printf("星期二\n");
case 3:
printf("星期三\n");
case 4:
printf("星期四\n");
case 5:
printf("星期五\n");
case 6:
printf("星期六\n");
case 7:
printf("星期天\n");
}
return 0;
}
我们发现怎么输入2,把剩下的都打印出来了,并没有按照要求实现分支的功能啊!!!
2.2.1 在switch语句中的break
其实在switch语句中,我们没办法直接实现分支,搭配break使用才能实现真正的分支
int main()
{
int day = 0;
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;
}
return 0;
}
这样就能正确实现分支的功能了,上述代码的执行逻辑是,我在键盘上输入2,然后与case后面的值进行匹配,对应打印星期二,遇到break,则直接跳出循环,我们调试看一下
再按一下F10
我们发现遇到break,直接跳出了循环
有时候我们的需求变了:
输入1-5,输出的是“weekday”;
输入6-7,输出“weekend”
那这时就应该这么书写代码
因为周一至周五都是weekday,所以我们只需在这种情况的最后一个case中加入break即可
break语句的实际效果是把语句列表划分为不同的分支部分
编程好习惯
在最后一个 case 语句的后面加上一条 break语句
(之所以这么写是可以避免出现在以前的最后一个 case 语句后面忘了添加 break语句)
2.2.2 default子句
如果表达的值与所有的case标签的值都不匹配怎么办?其实也没什么,结果就是所有的语句都被跳过而已。
程序并不会终止,也不会报错,因为这种情况在C语言中并不认为是个错误。
但是,如果你并不想忽略那些不匹配所有标签的表达式的值时该怎么办呢?
你可以在语句列表中增加一条default子句,把下面的标签
default:
写在任何一个 case 标签可以出现的位置。
当 switch 表达式的值并不匹配所有 case 标签的值时,这个 default 子句后面的语句就会执行。
所以,每个switch语句中只能出现一条default子句。
但是它可以出现在语句列表的任何位置,而且语句流会像执行一个case标签一样执行default子句。
2.2.3 练习
这里给大家五分钟的时间,想一想这个程序的结果是什么?
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;
}
补充:switch语句是可以嵌套使用的,break语句只会跳出一层循环
结果是:
在这里给大家讲解一下:
int main()
{
int n = 1;
int m = 2;
switch (n)//n = 1,走case 1的情况
{
case 1:
m++;//m = 3,没有break,按顺序往下执行
case 2:
n++;//n = 2
case 3:
switch (n)//switch嵌套,n = 2
{
case 1:
n++;
case 2://直接来这
m++;//m = 4
n++;//n = 3
break;//跳出内层switch循环
}
case 4://来到这
m++;//m = 5
break;跳出外层switch循环
default:
break;
}
printf("m = %d, n = %d\n", m, n);//直接来到这,打印 m = 5, n = 3
return 0;
}
3、循环语句
-
while
-
for
-
do while
3.1 while循环
我们已经掌握了,if语句:
if(条件)
{
语句;
}
当条件满足的情况下,if语句后的语句执行,否则不执行。但是这个语句只会执行一次。
由于我们发现生活中很多的实际的例子是:同一件事情我们需要完成很多次。那我们怎么做呢?
C语言中给我们引入了: while 语句,可以实现循环。
//while的语法结构
while(条件)
{
循环语句;
}
这个就是while循环的执行流程
比如我们实现:
在屏幕上打印1-10的数
int main()
{
int i = 1;
while (i <= 10)//条件是i的范围,符合则进来
{
printf("%d ", i);//打印i
i = i + 1;//i不断加1,再次回到判断条件符合则进行下一次循环
}
return 0;
}
上面的代码已经帮我们了解了 while 语句的基本语法,那我们再了解一下while语句中的break和continue
3.1.1 while语句中的break
接下来请大家思考以下代码的结果
int main()
{
int i = 1;
while (i <= 10)
{
if (i == 5)
break;
printf("%d ", i);
i = i + 1;
}
return 0;
}
运行的结果是下列哪一个呢?
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6 7 8 9 10
1 2 3 4 6 7 8 9 10
是第一个!
我们发现当i打印4以后,i = i + 1变成5,再次进入循环此时if条件为真,执行break,我们调试看一下,执行break前后代码该到哪里接着执行
现在是第20行,按F10执行下一步,我们看
黄色的箭头直接来到了24行,直接跳出了循环,再按一下F10程序就运行结束,所以打印1 2 3 4
总结:
break在while循环中的作用:
其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。
所以:while中的break是用于永久终止循环的。
3.1.2 while语句中的continue
接下来请大家思考以下代码的结果
int main()
{
int i = 1;
while (i <= 10)
{
if (i == 5)
continue;
printf("%d ", i);
i = i + 1;
}
return 0;
}
运行的结果又会是下列哪一个呢?
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6 7 8 9 10
1 2 3 4 6 7 8 9 10
是第一个!当i==5条件为真,执行完continue后,我们发现后面再也没有打印出来任何的值
我们再来看下面这个代码
我们把i=i+1的这一行代码挪到if之前,我们发现结果全然不同,但是结果中并没有打印5这个数,那我们调试一下来观察continue的作用
再次按F10
直接来到了循环的判断条件-45行,跳过了第50行打印i的这句代码,直接来到下一次循环
总结
continue在while循环中的作用就是:
continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分,进行下一次循环的入口判断。
3.1.3 实景演绎
在基本介绍完这些while语句中的语法问题时,用一个现实生活中的场景带大家再感受感受---密码匹配
先来介绍一下要用到的两个库函数---getchar和putchar
getchar - 从键盘获取字符 EOF - end of file - 文件结束标志 #define EOF (-1) 在函数读取失败的时候返回了EOF scanf 函数读取成功,返回的是读取到的数据的个数,读取失败返回EOF getchar 读取成功返回字符的ASCII码值,读取失败返回EOF ctrl+Z - 回让scanf 或者 getchar 返回EOF putchar用来打印字符
从图片中我们得知,putchar和用printf打印字符是一个作用
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
putchar(ch);
}
return 0;
}
实现的效果如图
我们发现都没有让我们输出Y直接就匹配失败了这是为什么呢?
那我们现在修改一下这个代码,把\n读走就可以了
这样就成功实现我们的功能了,那我们换个复杂的密码试一试
相同的问题再一次出现了,又一次确认失败,这是因为
scanf会把空格前面的拿走,而更改后加入的getchar();把空格读取走了,剩下的是abc\n所以又一次失败了,而我们需要让getchar读取走缓冲区里的所有内容一直到\n,把\n也读取走
这样我们想实现的功能就彻底实现啦!!!
我们再来看一个代码
int main()
{
char ch = '\0';
while ((ch = getchar()) != EOF)
{
if (ch < '0'|| ch > '9')//字符在内存中是以ASCII码存储的,符合判断条件的都是除了数字的其他字符
continue;
putchar(ch);
}
return 0;
}
//这个代码的作用是:只打印数字字符,跳过其他字符的
输入1以后并没有换行,输入q光标就来到下一行了,这里的1后面有个回车但是不打印,所以1的后面没有换行
3.2 for循环
我们用while循环在屏幕上来打印1-10的数字
3.2.1 语法
for(表达式1; 表达式2; 表达式3)
循环语句;
表达式1
表达式1为初始化部分,用于初始化循环变量的。
表达2
表达式2为条件判断部分,用于判断循环时候终止。
表达式3
表达式3为调整部分,用于循环条件的调整。
那我们用for循环再来实现一下在屏幕上打印1-10
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
printf("%d ", i);
}
return 0;
}
我们把这两种循环比较能发现,for循环将影响循环的三个部分整合到了一起,图中相同颜色是对应的
给大家看一下for循环的程序流程图,是不是对这个循环有了自己的认识呢?
我们在初始化以后,就是在反复的进行判断-循环体语句执行-循环变量调整这三部分
可以发现在while循环中依然存在循环的三个必须条件,但是由于风格的问题使得三个部分很可能偏离较
远,这样查找修改就不够集中和方便。所以,for循环的风格更胜一筹;for循环使用的频率也最高。
3.2.2 for循环的break
我们发现在for循环中也以出现break,他的意义和在while循环中是一样的。
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
break;
printf("%d ", i);
}
return 0;
}
这个代码的运行结果是什么呢?
这里的break也是直接跳出循环,大家可以自己动手调试一下
3.2.3 for循环的continue
我们发现在for循环中也以出现continue,他的意义和在while循环中是有区别的
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
continue;
printf("%d ", i);
}
return 0;
}
这个代码的运行结果是什么呢?
这里的continue就有所不同了,我们说while循环中的continue是跳过后续的代码,来到判断条件的地方。而for循环这里则是来到了循环变量调整的地方,正是因为循环变量调整这一部分位置的不同才导致了不一样的结果
3.2.4 for语句的循环控制变量
建议1:不可在for循环体内修改循环变量,防止 for 循环失去控制。
用代码为大家讲解
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i = 5)//我在这里对i进行修改,对i赋值
continue;
printf("%d ", i);
}
return 0;
}
我们发现结果死循环了,这是因为在循环体里对i赋值以后,下一次循环判断条件始终都为真,为真就执行continue,循环往复,最终死循环了
建议2:for语句的循环控制变量的取值采用“前闭后开区间”写法。
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", i);
}
return 0;
}
int main()
{
int i = 0;
for (i = 0; i <= 9; i++)
{
printf("%d ", i);
}
return 0;
}
上述两种代码都对,但我们更推荐第一种,没有特殊的意义,第一种可以直观的告诉我们循环了多少次
补充:
int main()
{
for (int i = 0; i <= 9; i++)//这种循环变量初始化的写法在C99标准中引入
//支持C99标准的编译器可以这么使用,这种写法是在C++中可以使用的
//如果你的编译器不支持C99标准,那么这种写法就是不行的
{
printf("%d ", i);
}
return 0;
}
3.2.5 一些for循环的变种
变种1:
int main()
{
//初始化和调整部分的省略就是啥都不做
//判断部分省略了,意味着判断是恒为真的
//建议不要随便省略
for (;;)
{
printf("hehe\n");
}
return 0;
}
运行以后我们发现程序死循环了
变种2:
int main()
{
int i = 0;
int j = 0;
//这里打印多少个hehe?
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
printf("hehe\n");
}
}
return 0;
}
一共是9个hehe,外层for循环执行3次,每一次外层for循环都会使内层for循环执行3次,所以一共是3*3=9次
变种3:
int main()
{
int i = 0;
int j = 0;
//如果省略掉初始化部分,这里打印多少个hehe?
for (; i < 3; i++)
{
for (; j < 3; j++)
{
printf("hehe\n");
}
}
return 0;
}
为什么一共才打印了3个hehe呢?因为i=0时第一次进入循环,内层for循环使循环变量j从0最终加到3,此时内层循环结束,执行第二次外层循环,此时i是2,再次进入内层for循环,但这次因为没有初始化循环变量,j还是3内层循环不会执行,所以外层不论多少次循环,内存循环也不再执行,所以只打印出了3个hehe
变种4:
int main()
{
int x, y;
for (x = 0, y = 0; x < 2 && y < 5; ++x, y++)
{
printf("hehe\n");
}
return 0;
}
可以使用多余一个变量控制循环,三个影响循环的部分可以是表达式,表达式拥有几个变量我并不关心,我只关心哪个是初始化,哪个是判断,哪个是调整
3.2.6 一道经典笔试题
//请问循环要循环多少次?
#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是把0赋值给k,这个表达式的结果是0,0为假所以判断失效不成立,所以执行0次
3.3 do while循环
3.3.1 do while循环语句语法
do
循环语句;
while(表达式);
3.3.2 执行流程
int main()
{
int i = 1;
do
{
printf("%d ", i);//上来就执行依次循环
i = i + 1;
} while (i <= 10);//判断条件
return 0;
}
3.3.3 do while循环语句的特点
循环至少执行一次,使用的场景有限,所以不是经常使用
我们再来用do while循环实现打印1-10的数字
int main()
{
int i = 1;
do
{
printf("%d ", i);//上来就执行依次循环
i = i + 1;
} while (i <= 10);//判断条件
return 0;
}
3.3.4 do while循环中的break
int main()
{
int i = 1;
do
{
if (5 == i)
break;
printf("%d ", i);
i = i + 1;
} while (i <= 10);
return 0;
}
我们来调试一下就知道这里的break是什么作用了
现在符合if的条件来到了第22行,再按一下F10
我们发现直接跳出循环来到了27行,这里的break也是跳出循环的意思
3.3.4 do while循环中的continue
int main()
{
int i = 1;
do
{
if (5 == i)
continue;
printf("%d ", i);
i = i + 1;
} while (i <= 10);
return 0;
}
我们来调试一下就知道这里的continue是什么作用了
现在符合if的条件来到了第22行,再按一下F10
此时来到了第39行,这里的continue则是跳过循环中的后续代码,来到了while判断条件的地方
3.4 练习
在这里给大家出了几道小题,大家动手做做,不明白的可以评论私信我
-
计算 n的阶乘。
//不考虑溢出 int main() { int n = 0; scanf("%d", &n); int i = 0; int ret = 1; for (i = 1; i <= n; i++) { ret = ret * i; } printf("%d\n", ret); return 0; }
-
计算 1!+2!+3!+……+10!
//方法一 int main() { int i = 1; int ret = 1; int sum = 0; for (i = 1; i <= 10; i++) { ret = ret * i; sum = sum + ret; } printf("%d", sum); return 0; }
//方法二 int main() { int n = 0; int i = 0; int ret = 1; int sum = 0; for (n = 1; n <= 3; n++) { ret = 1;//防止累计,重新初始化 for (i = 1; i <= n; i++) { ret = ret * i; } sum += ret; } printf("%d\n", sum); return 0; }
-
在一个有序数组中查找具体的某个数字n。
//第一种方法是从头到尾遍历,挨个进行比较 int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//升序 int k = 17; int i = 0; for (i = 0; i < 10; i++) { if (arr[i] == k) { printf("找到了,下标是:%d\n", i); break; } } if (i == 10) { printf("找不到了\n"); } return 0; }
//第二个方法是折半查找算法,对初学者有一些难度,掌握好思路是重点 //首先讲一种获得数组元素个数的方法---涉及到sizeof int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; printf("%d", sizeof(arr));//计算的数组的总大小,单位是字节 printf("%d", sizeof(arr[0]));//一个int类型的元素大小是4个字节 printf("%d", sizeof(arr) / sizeof(arr[0]));//数组中的元素个数 return 0; }
int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//所查找的有序数组 int k = 7;//7是我们要找的元素 int sz = sizeof(arr) / sizeof(arr[0]);//获取元素个数 //1、获取左右范围 int left = 0; int right = sz - 1; int flag = 0;//flag的作用是标志是否找到了 //2、循环查找 while (left <= right) { int mid = (left + right) / 2;//根据下标找到中间值 if (arr[mid] == k) { printf("找到了,下标是:%d\n", mid); flag = 1;//找到则修改标记 break;//直接跳出循环 } else if (arr[mid] < k) { left = mid + 1;//更新查找范围 } else { right = mid - 1;//更新查找范围 } } //3、查找失败 if (flag == 0) { printf("没找到\n"); } return 0; }
4.编写代码,演示多个字符从两端移动,向中间汇聚。
//实现效果
//Learn programming with me
//#########################
//L#######################e
//Le#####################me
//Lea################### me
//Lear#################h me
//Learn###############th me
//...
//Learn programming with me
#include <string.h>
#include <windows.h>
//Sleep需要一个windows.h的头文件
#include <stdlib.h>
//system需要一个stdlib.h的头文件
int main()
{
char arr1[] = "Learn programming with me";
char arr2[] = "#########################";
int left = 0;
int right = strlen(arr1)-1;//strlen是计算字符串长度的库函数,计算\至0之前
//这里不能用sizeof,因为字符串末尾有\0,要想使用需要把\0也减去
//int right = sizeof(arr1) - 2;
while (left<=right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000);//单位是毫秒,让执行的上下语句间隔1000毫秒=1秒
system("cls");//system函数可以执行系统命令,cls是清理屏幕,使结果一行一行展现
left++;
right--;
}
printf("%s\n", arr2);
return 0;
}
5.编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则
提示登录成,如果三次均输入错误,则退出程序。
//假设密码是:"123456"
int main()
{
int i = 0;
char password[20] = {0};
for (i = 0; i < 3; i++)//题目要求只能登陆三次,所以循环三次
{
printf("请输入密码:>");
scanf("%s", password);
//在这里强调一下,判断密码是否正确
//if(password == "123456")这是错误的
//两个字符串比较相等是不能使用==的,应该使用strcmp库函数
//strcmp返回0表示2个字符串相等
//strcmp返回>0的数字,表示第一个字符串大于第二个字符串
//strcmp返回<0的数字,表示第一个字符串小于第二个字符串
//两个字符串的大小是通过比较相同位置的字符的ASCII码值得出的
//abcd\0
//abq
if (strcmp(password, "123456") == 0)
{
printf("登录成功\n");
break;
}
else
{
printf("密码错误\n");
}
}
//如果登录成功则i一定小于三,如果循环结束来到这i=3.则是登陆失败,退出程序
if (i == 3)
printf("退出程序\n");
return 0;
}
3.5 猜数字游戏
在这里把我们前面所学的所有知识点整合在一起,在这里从无到有为大家做一个小游戏
首先我们要确定这个游戏我们要实现的功能
猜数字游戏
1、电脑要随机生成一个数
2、猜数字
a>猜大了,提示猜大了,继续猜
b>猜小了,提示猜小了,继续猜
c>猜对了,恭喜你,猜对了,继续游戏
3、玩完一把可以接着玩,不用退出程序
那知道了这些功能以后,我们就可以开始写代码了
#include<stdio.h>
void menu()//菜单函数,在屏幕上打印菜单,让游戏界面显得美观
{
printf("***************************\n");
printf("***** 1.play 0.exit *****\n");
printf("***************************\n");
}
int main()
{
int input = 0;
menu();//调用菜单函数
printf("请选择:>");
scanf("%d", &input);
switch (input)//根据输入的值匹配对应的情况
{
case 1:
printf("开始游戏\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
return 0;
}
我们在未来的开发种应该做到边写边调试,而不是从第一行写到最后,结果一编译200个错误
我们调试
我们发现运行没有问题,那我们接着会发现要求是玩完一把可以接着玩,所以我们需要用到循环,而根据实际情况,需要先打印菜单,所以用do while循环比较合适,同时我们要把游戏的功能封装在game函数中,在case1中实现
void game()
{
//1. 生成随机数
int ret = rand();//可以生成随机数,随机数的范围是:0~32767
printf("%d\n", ret);
}
我们双击选中点击速览定义
我们发现这是定义好的,最大值就是0x7fff
这个0x7fff就是十进制的32767,大家下去可以用计算器算一下
然后我们运行看一看
我们退出程序再次运行发现跟上面的结果一致
rand函数确实可以给我们生成随机数,在一次运行内部好像很随机,但本次运行和下一次运行的结果却有问题,那么问题出现在哪里呢?其实是我们这个函数用错了,那这个函数我们怎么用呢?
我们通过在cplusplus.com中就可以找到这两个函数的介绍,我们发现在rand函数调用前需要调用srand,用来设置随机数的生成起点
而srand函数是初始化随机数的生成器,返回值是空,参数类型是unsigned int
那是不是意味着直接使用它就可以了呢?
我们发现每次生成的随机数都是41,是因为srand(1)中的1是固定的,改称2或者100运行会是不同的值
因为里面的数是不变的所以我们生成的随机数也是不变的,只有里面是随机数,生成的随机数才是变的
所以我们要给srand传一个随机数,然后又用rand生成一个随机的值,这就形成了一个先有鸡还是先有蛋的问题,我们本来要生成一个随机数,结果srand又需要一个随机数,有点尴尬
其实我们不是要传随机数,是给srand传一个变化的值
void game()
{
RAND_MAX;
//1. 生成随机数
srand();//要给srand传递一个变化的值,计算机上的时间是时刻发生变化的
//这时候就可以使用时间戳
int ret = rand();//可以生成随机数,随机数的范围是:0~32767
printf("%d\n", ret);
}
任何一个时间都可以转换一个对应的值(单位为秒的数字),任何一个值都可以转换成一个对应的时间,时间在变,时间戳就变,这个值就是时间戳
那我们是不是就可以传一个时间戳给srand函数呢?那么这个时间戳怎么产生呢?
C语言中的time函数可以返回一个时间戳,那我们怎么用呢?
参数是指针类型,不用就可以传一个空指针,返回值我们得用,返回的就是时间戳
我们发现time_t本质上就是long long整型
void game()
{
RAND_MAX;
//1. 生成随机数
srand((unsigned int)time(NULL));//要给srand传递一个变化的值,计算机上的时间是时刻发生变化
//time函数可以返回一个时间戳,返回值类型是time_t
//srand函数返回值类型是unsigned int
//在这里我们将他强制转换
int ret = rand();//可以生成随机数,随机数的范围是:0~32767
printf("%d\n", ret);
}
当我们运行会发现确实生成了随机数,但是每个之间离得很近,我们快速运行几次就会发现出现了相同的随机数,当前这种生成随机数的方法也是有问题的,我们确实已经找到了生成随机数的方法,但是这个srand不能频繁使用,不能说每一次生成随机数都设置一个随机数生成起点,这个srand函数在整个工程里面只调用一次就可以了,所以我们将它挪到主函数中,这样它只执行一次,执行完以后,后面就可以放心大胆的使用rand函数了,这是规则,就应该这么用,这样生成的随机数才非常的随机
#include <stdio.h>
#include <stdlib.h>
#include <time.h>//time函数需要头文件
void menu()
{
printf("***************************\n");
printf("***** 1.play 0.exit *****\n");
printf("***************************\n");
}
void game()
{
RAND_MAX;
//1. 生成随机数
int ret = rand();//可以生成随机数,随机数的范围是:0~32767
printf("%d\n", ret);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//要给srand传递一个变化的值,计算机上的时间是时刻发生变化
//#define NULL ((void *)0) NULL的定义
//time函数可以返回一个时间戳,返回值类型是time_t
//srand函数返回值类型是unsigned int
//在这里我们将他强制转换
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;
}
运行发现,我们按多快,都是很随机的数
为了让游戏更有意思,我们将随机数的范围设置在1-100之间
void game()
{
RAND_MAX;
//1. 生成随机数
//int ret = rand();//可以生成随机数,随机数的范围是:0~32767
int ret = rand()%100+1;
//任何一个数字模100的余数的取值范围只可能是0-99
//给这样的余数+1就是1-100
}
同样猜数字也需要根据一开始我们确定的功能来实现,需要用到循环
void game()
{
RAND_MAX;
//1. 生成随机数
int ret = rand() % 100 + 1;//可以生成随机数,随机数的范围是:0~32767
//2、猜数字
int guess = 0;
while (1)//判断条件-死循环,一直猜
{
printf("请猜数字:>");
scanf("%d", &guess);
if (guess > ret)
{
printf("猜大了\n");
}
else if (guess < ret)
{
printf("猜小了\n");
}
else
{
printf("恭喜你,猜对了\n");
break;//猜对了循环停下里
}
}
}
完整代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void menu()
{
printf("***************************\n");
printf("***** 1.play 0.exit *****\n");
printf("***************************\n");
}
void game()
{
RAND_MAX;
//1. 生成随机数
int ret = rand() % 100 + 1;//可以生成随机数,随机数的范围是:0~32767
//2、猜数字
int guess = 0;
while (1)
{
printf("请猜数字:>");
scanf("%d", &guess);
if (guess > ret)
{
printf("猜大了\n");
}
else if (guess < ret)
{
printf("猜小了\n");
}
else
{
printf("恭喜你,猜对了\n");
break;
}
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//要给srand传递一个变化的值,计算机上的时间是时刻发生变化
//#define NULL ((void *)0) NULL的定义
//time函数可以返回一个时间戳,返回值类型是time_t
//srand函数返回值类型是unsigned int
//在这里我们将他强制转换
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;
}
运行来玩一玩吧
这样我们就成功实现啦!!!快给自己鼓个掌吧!
3.6 goto语句
C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。
从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。
但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。
例如:一次跳出两层或多层循环。
多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。
goto语言真正适合的场景如下:
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
…
error://标签,goto语句可以跳到程序前面,也可以跳到程序后面
if(disaster)
// 处理错误情况
举个例子:
int main()
{
again://标签,顶头写
printf("good good study\n");
printf("day day up\n");
printf("一起学习\n");
goto again;
return 0;
}
调试我们发现,这个运行又一次来到了75行,这个程序就死循环了
同时
下面是使用goto语句的一个例子,一个关机程序
//关机程序
//1. 程序运行起来后,1分钟内电脑关机
//2. 如果输入:我是猪,就取消关机
#include <stdlib.h>
#include <string.h>
int main()
{
char input[20] = { 0 };//用一个字符数组存输入的内容
//程序倒计时关机
system("shutdown -s -t 60");
//system可以执行系统命令,而这里的shutdown -s -t 60就是还有60秒关机
again:
printf("请注意,你的电脑在1分钟内关机,如果输入:我是猪,就取消关机\n");
scanf("%s", input);//输入
if (strcmp(input, "我是猪") == 0)//字符串比较用strcmp函数
{
system("shutdown -a");//取消关机命令
}
else
{
goto again;//直接跳转,循环输入
}
return 0;
}
我们按住键盘的win+r键,
点击确定
在这里我们可以各种各样的命令来控制电脑
点击回车,我们发现系统提示关机,这时我们赶紧取消,用shutdown -a即可
而system就是在C语言程序中执行系统命令
好啦!!!到这里第二个大部分就彻底结束啦!!!再接再厉学到这你已经很厉害了!!!给自己竖一个大拇指!!!你是最棒的!!!今天小周就带大家学到这里。小周写一篇高质量详细的博客也也花了好多时间,看到这里请动动你的小手来为小周点赞推广评论一下吧!!!你们的点赞推广评论是我继续输出高质量博客的动力!!!谢谢大家!!!
欲知后事如何,且听下回分解