废话不多说,咱们马上开始。
三、C语言基础下(从交互到数组)
一、输入输出
从上一章的例子可以看出,很多时候我们需要向程序输入一些数据,或者希望程序将一些数据以可视化的方式呈现在我们眼前。人机的交互就显得十分重要。
C语言本身不提供输入输出的语句,但它提供了一个标准库,即说明书,里面定义了一些输入输出的方法,如printf和scanf。本章将介绍这些方法的用法。
使用这些方法之前,要包含这个说明书哦(#include<stdio.h>)。
- printf
printf用来向小黑窗输出一些数据。
用法:
a. printf(”输出”);——将“”里的东西打印出来。
注:转移字符也是可以打印的,如果在“”中出现/a,打印的时候就会有提示音,如果是/n,后面的内容就会换行打印。具体可以对照前面的转义字符表。
b.printf(“包含格式控制”,输出列表);
还记得前面的例子我们如何打印周长的值的吗,就是用的这个方法,当我们想打印变量的值时,就可以采用这种格式。
格式控制是什么?简单来说,就是在“”中代替变量的东西,比如我们想打印一个整型变量a的值,如果直接在“”中出现a,printf会认为你本身就想输出a这个字符,而不是变量,这时就需要格式控制字符出马了。
格式控制字符有下面几种,分别对应不同的数据类型和情况。
%d | 输出带符号十进制整数 |
---|---|
%c | 输出一个字符 |
%s | 输出一个字符串 |
%f | 输出实数(带小数)(注:只能输出6位小数) |
%o | 以八进制形式输出一个整数 |
%x | 以十六进制形式输出一个整数 |
%u | 输出无符号型数据 |
%e,%E | 以指数形式输出实数 |
%g,%G | 自动匹配实数输出方式(小数形式或者指数形式) |
在%d,%f,%x,%o可以加l前缀变成%ld,%lf,%lx,%lo,表示长型。
对于%c,可加数字指定宽度,如%5c,表示输出5个长度的字符,前面用空格补齐。
对%f,可以有形如%m.nf的形式,其中m表示输出占的宽度,n表示输出的小数,采用四舍五入的形式。如%8.3f表示输出一共长为8,其中有3位小数。位数不够时,在前面补空格(后对齐),如果是%-m.nf,则是在后面补空格(前对齐)。
当然,格式控制字符可以夹杂在普通字符之中,也可以在一个printf中输出多个变量,要注意和输出列表相对应就行,第一个控制字符对于输出列表中的第一个变量。
如果想输出%这个字符,和输出/类似的,要连续写2个%%,这时才会输出一个%字符。
- scanf
scanf用来从键盘上输入东西给程序。其一般格式为:
scanf(“格式控制“,地址列表);
格式与printf一样,以%开头,可以与其他字符混合使用,但注意此时输入必须完全一致。如“ “中是“ok56%d”,由前面可知我们想输入一个整型,但此时直接输入是不能起作用的,必须按照写的内容先输入ok56之后再输入的数字才算整型变量的内容。
注意,这里是地址列表,和上面的变量列表有所不同。
具体不同在哪里?就是要把输入的值给哪个变量,哪个变量前面要加上&一个符号,从而构成地址列表。
如scanf(“%d”,&a);表示输入一个整型变量,并给a。
注意,不管scanf还是printf,变量与格式类型都要一直,否则无法正确输出或输入。
接下来引入地址的概念。
我们前面已经说过,变量开辟在内存的某个区域,里面存放的值就是这个变量的值,我们通过变量名对这片区域操作。那么我们能否知道这个区域在哪里呢?实际上是可以的,虽然我们无法指定这个变量开辟在内存的什么地方,但我们可以知道它被开辟在哪里,变量开辟在内存的什么地方就是变量的地址,就好比房子与门牌号,内存空间就是房子,里面住着变量,门牌号就是地址。那么怎么取得这个门牌号?我们用一种新的运算符&,&a就表示a的地址。当然我们可以看看。
#include<stdio.h>
int main()
{
int a,b;
printf("%x\n%x",&a,&b);
return 0;
}
a和b的地址就显示出来了。
虽然现在看起来这个地址没啥用,在后面可是有非常重要的作用,和C语言的灵魂——指针相互配合,能产生极强的combo。当然这都是后话,循序渐进嘛。
我们也可以一次输入多个变量的值。注意匹配就行。对于数字类型的输入,两两之间要以空格、回车、tab键分割来区分,对于字符类型的输入,因为空格、回车、tab键也是字符,或者说从键盘上输入的全是字符,所以连续输入就行了。
介绍了用处最多的两种输入输出,下面介绍一些其他的输入输出。
- putchar:输出一个字符,putchar(c),c为字符型变量。
- getchar:输入一个字符,没有参数,需要有变量接收,c = getchar(),c为字符型变量,接受来着键盘输入的一个字符。
注:这里讲解一下接收的概念,我们从键盘上向程序输入一个东西,程序就需要用一个东西来接收我们的输入,通常是用变量来接收。
- puts():输出一个字符串。
- gets():输入一个字符串。
注:大家会发现,数据类型虽然有字符串,但C语言并没有相应的变量类型,不像别的语言有string这中字符串的变量类型,那么C语言怎么实现字符串的操作?是通过数组来实现的,这里先埋个伏笔。
二、程序结构
在这一章,我们来从整体程序的角度,看看程序一般由什么部分构成。
程序结构分三大块,选择结构、循环结构、顺序结构
我们前面写的都是顺序结构,即从头写到尾,这个结构是线性的。
- 选择结构
还记得三目运算符吗,它通过判断逻辑表达式的真假,实现执行不同的代码的作用,这就是选择结构。选择结构通过判断,根据判断的结果执行不同的代码块。
- if选择结构的基本构成:
if(判断条件)
{
代码块a
}
判断条件是逻辑表达式或其组合,当判断条件为真(非0值)时,程序进入大括号执行代码块a,否则跳过不执行代码块a。
if(判断条件)
{
代码块a
}else{
代码块b
}
判断条件是逻辑表达式或其组合,当判断条件为真(非0值)时,程序进入大括号执行代码块a,否则执行代码块b。
If里面可以套if,不断细分判断情况,if——else也可以逐级使用,实现分级过滤的效果。
如:
if(判断条件1)
{
代码块a
}else if(判断条件2)
{
代码块b
}else{
代码块c
}
判断是否符合条件1,不符合的话,判断是否符合条件2,不符合的话,就执行最后的else,即代码块c。这种结构可以多级使用。
If与else的匹配:当然实际上我们在写代码的时候会有缩进,很容易看出if和哪个else是对应的,但架不住有人喜欢考啊,这里说一下,else总是和最近的if匹配。
关系运算符的优先级:
关系运算符优先级低于算术运算符,高于赋值运算符。
- switch语句实现多分支
对于多种彼此互斥的情况,如一等二等三等,我们当然可以使用上面的if——else进行分级判断
if(是一等)
{
代码块a
}else if(二等) //不是一等,还可能是二等三等,对二等进行判断。
{
代码块b
}else{ //不是一二等,是三等
代码块c
}
C语言还提供了一种新的判断的结构,即switch结构
基本构成:
switch(判断变量)
{
case 情况1:
case 情况2:
default:
}
这里的case可以有很多,取决于要判断的情况,这里只举例写了两个。
switch判断要判断的变量的值,对应下面的哪个情况,比如待判断的变量值为5,就走case 5:这一条分支(如果写了case 5:这一条分支的话)。如果都没有,在有default的情况下执行default的程序,没有的话就跳出switch。
首先需要指出,这里判断的变量只能是整数,所以a只能为整型或字符型,这一点极大的限制了switch的使用。
其次,这里的情况是要和待判断的变量的值相匹配。由于我们必须指明一种情况来对应一个case,(a<1是不能当case的情况的,即case的情况也必须是整数)所以我们只有知道确切的条件判断情况才能使用switch语句,对于a<1这种判断switch是无法使用的。
下面对switch语句进行进一步理解。
switch(判断变量)
{
case 情况1:
case 情况2:
default:
}
这里的case M:的作用只是起到一个标号的作用,就是告诉switch这里有一个入口,进入这个入口的条件是待判断的变量的值等于M,我们可以罗列所有待判断变量的取值,这样可以保证每次都进入一个case,当然很多时候罗列是不可行的,我们采用default分支,当switch发现没有入口对应这个变量的值时,他就会进入default的入口。
注意:case只是起到标号的作用,告诉入口,但并不告诉出口。这什么意思呢,就是当switch进入一个case入口后,它不知道什么时候出去,我们当然是希望它只执行这个case里的语句,当它执行完就不再执行其他case和default的语句而是直接跳出switch,而实际上如果不加控制,switch会从进入的case语句一直向下执行,把写在这个入口case语句下面的case和default都执行了,那这样根本没起到分支的效果啊(当然这种特性有也奇用)。所以我们需要添加出口,来告诉switch进入后从哪里出,既然是先进入才出,那出口肯定是要写在case语句里,而且由于上面的原因,每个case都要一个出口(default一般可以不要,因为它一般写在switch最后,执行完switch也就执行完了,不需要额外的出口)。C语言实现出口的语句是break,所以我们在每个case里都加上break,程序就能顺利执行了,所以程序一般实际上是这样的:
switch(判断变量)
{
case 情况1:代码;break;
case 情况2:代码;break;
default:代码;
}
代码训练与详解
首先来一个分段函数;
例:实现sgn函数(x<0是为-1,x=0时为0,x>0时为1)
我们用if来实现(switch不行,因为x<0是无穷多的情况,不能用case表示)
#include<stdio.h>
int main()
{
double x=0;
double y=0;
printf("Input the x:");
scanf("%lf",&x);
if(x<0)
{
y = -1;
}else if(x == 0)
{
y = 0;
}else{
y = 1;
}
printf("y = %-1.0lf",y);
return 0;
}
有了上面分段函数的定义就很好写了。先判断x是否小于0,不小于的话再判断x是否等于0,要是也不等于,说明x是大于0的,再把各个部分的代码块补充完成,就OK了。最后printf采用m.n的形式输出,因为一定是没有小数的,所以后面的一堆0就不必显示了。
例:实现大小月的判断
这里我们采用switch分支,因为只有12个月是有限的。
老样子,先上码。
#include<stdio.h>
int main()
{
int month=0;
printf("Input the month:");
scanf("%d",&month);
switch(month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
printf("It has 31 days.");
break;
case 2:
printf("It usually has 28days.");
break;
case 4:
case 6:
case 9:
case 11:
printf("It has 30 days.");
break;
default:
printf("Error input\a");
}
return 0;
}
这里我采用了前面case的奇妙用法,因为虽然入口需要彼此不同(一条路(入口)怎么走向2个方向呢,能走向2个方向必然要有分支),但可以指向同一个地方(条条大路通罗马)。
我们当然可以这样写
等等,但这里我们不让程序结束,而是引导所有的入口指向统一地方(代码块),就可以实现多输入,单输出的效果。
注意,每一块代码块完成后,要给出出口防止程序继续执行别的代码块。
大家还可以发现,case的情况的顺序可以随便写的,因为它只是个入口,而与它本身处在的位置无关,甚至default放开头当然也没有问题。
大家都知道一年有12个月,那12个case足以包含所有情况了,为什么还要一个default?这里是为了程序的健壮性。当用户输入一个不在1到12之间的数时,我们希望给与输入错误的反馈,而不是啥也不管继续执行,防止后续程序因这个判断的失效而崩溃。(我喜欢在error的地方加上提示音起到警示的作用,当然这个就看个人啦。)
当然,这个程序还可以完善,比如闰年2月29天,平年2月28天,从而升级成对任何年份都成立的月份天数判断程序。
我们要先判断平闰年,这个判断比较复杂,但写起来缺很短。
当然if也可以实现,但这里我们采用逻辑表达式,因为用逻辑表达式更为精炼。
例:实现平闰年的判断
首先,我们知道闰年大特性:能被4整除,百年能被400整除,由此我们可以写出这样的判断:
if(year%4 == 0 || year%400 == 0)
{
printf(“闰年”);
}
整除代表着余数为0。这样的代码看起来没啥,但细看就会发现问题。能被400整除就一定能被4整除,所以由前面的知识,后面的判断根本不会执行。让我们来看看我们的初心:我们希望百年能被400整除,这里遗漏了一个点:百年,所以我们要先判断百年,再判断能被400整除吗?
if((year%4 == 0 && year%100!=0) || year%400 == 0)
{
printf(“闰年”);
}
前面用来判断是否是百年。
- 循环结构
当我们希望程序循环执行某一块程序,如循环输出,循环相加。我们要是使用顺序执行,就必须写很多行形式相同的代码,当然我们也可以使用循环结构来简化编程。
循环结构就是让程序循环执行一块程序。
C语言循环结构的基本框架:
- while循环
while(循环条件)
{
循环体;
}
在while语句中,while首先判断循环条件是否满足,如果满足,就执行一边循环体,再判断循环条件是否满足,再进行与上面同样的操作。
如果循环无法结束,即循环条件不可能为假,那么这个循环会一直执行下去,称为死循环。
这是应该避免的。所以说,循环条件一般要包含变量,这个变量是要在循环体中发生改变的,这样才有可能跳出循环。 - do——while循环:
do
{
循环体;
}while(循环条件);
这种结构与上面是相同的,只是不同的是先执行一边循环体,再判断循环条件是否成立。 - for循环(很重要)
基本结构:
for(初始化;循环条件;循环调整)//三部分
{
循环体;
}
循环变量:参与循环条件判断或者循环体的变量。
初始化:一般是对循环变量进行初始化,保证正确的循环次数,可以同时对多个变量初始化,彼此用逗号隔开。
循环条件:判断继续循环的条件。
循环调整:每轮循环都会执行的部分,通常用来使循环变量发生改变。
for语句的执行过程:先进行初始化,再判断循环条件是否满足,如果满足,再执行循环体,再执行循环调整,然后再判断,一直反复直到跳出。
for循环的三部分都可以省略,但分号一个不能省。
eg:for(i=0;i<10;i++)
表示i的初始值为0,每轮循环结束后i++,当i=10是中止循环,实际循环10次。
上述的所有循环种类之间可以相互嵌套。
大家可以把循环想成一个环形公路+一个分支路口,当循环条件满足时一直在环形公路里跑,当循环条件不满足时,就跑出环形公路。
C语言还支持对这个循环公路进行修改。如果我们希望提前结束循环,就相当于在环形公路上添加出口,即break语句。当然我们也应当添加一个分支路口,这样就实现可控的出口,当条件满足就从break出口跳出循环,不满足还是要走原来的环形公路,这是什么——if条件分支。break语句通常放在if条件中,要是直接加在循环中的话,程序跑到break一定会跳出,就起不到循环的作用了。
我们还可以对环形公路添加捷径,让它从某一块直接返回开头,这个添加捷径的语句是continue。它的执行将使程序跳过它后面的所有循环代码直接回到循环开头,进行新一轮的循环。同样的,应当对这个捷径加以控制,即放在if条件分支中,来保持循环主体的整体性(直接写在循环中,它后面的循环程序永远不会执行,那不就白写了吗。)
对于循环嵌套,break和continue只能对自己所处的循环起作用。相当于大公路套小公路,你的分支出口和捷径都是加在其本身所处的循环公路上的,所以只对本身起作用。
对于循环嵌套是很重要的一块内容,设计难度也比较大,本教程还是以入门为主,编程不想将太难的,各位读者应注意这个部分。
代码训练与详解
学完了所有的程序结构,我们终于可以写出一些像样的代码,实现一些比较复杂的功能了。
例:显示不超过任意整数的水仙花数
所谓的“水仙花数”是指一个三位数其各位数字的立方和等于该数本身,例如153是“水仙花数”。
这里我们采用强硬的枚举方法,即一个一个试,计算机最擅长这种操作了。
#include<stdio.h>
int main()
{
//输入上限数字
int i=100;
int num=0;
printf("Input the num:");
scanf("%d",&num);
//枚举
while(1)
{
int bai = i/100;
int shi = (i%100)/10;
int ge = (i%10);
if(i == bai*bai*bai+shi*shi*shi+ge*ge*ge)
{
printf("\n%d",i);
}
if(i>num)
{
break;
}else{
i++;
}
}
return 0;
}
这里bai,shi,ge分别用来提取i的百位十位个位,运用整型除整型,结果没小数的特点,除几十就是取纳位,如除100就是取百位,除1000就是取千位,当然前提是这一位必须是最高位,如果是个千位的数,你除100得到的就是千位和百位构成的一个大于10的数,这显然不是我们想要的。所以我们用取余的方法得到除去哪位剩下的数,对100取余就是去掉百位,以此类推。
上面我们采用了在死循环中(while(1)恒成立)添加出口的形式。当然也可以将判断写在while中。
例:编写计算整数次幂的程序
#include<stdio.h>
int main()
{
double ans=1;
double di;
int zhi;
int i;
printf("输入底数:");
scanf("%lf",&di);
printf("输入指数:");
scanf("%d",&zhi);
for(i=0;i<zhi;i++)
{
ans*=di;
}
printf("%lf",ans);
return 0;
}
只要抓住乘幂就是连乘,就OK。
现在它还是个程序,大家知道我们很多时候需要这种乘幂的运算,每用一次乘幂,就意味着我们需要这些代码一次,一个程序可能需要很多次乘幂运算,每次这么多代码,程序肯定又臭又长,这是编程应当避免的,编程应当最求美感,让人看得赏心悦目。那么有没有解决办法,当然有,就是封装成函数,具体内容将在函数中讲解,这里先引入一下。
三、 数组
恭喜大家,来的了C语言基础的最后一部分,学完数组大家差不多就算跨在门槛上了。当然数组也是所有目前学过的最难的一部分。让我们开始吧。
数组就是容器,里面放着很多同样的东西。
我们前面说过,变量是一个房间,那数组就是一栋楼,里面放着元素。
我们先从一维数组说起。
- 一维数组
定义:int a[num];
int代表这个数组的元素的类型,因为数组的元素要求是一样的,所以不能把一个double的放在int数组里。这里只是举例,各种类型的数组都是可以的。
a是数组名,num是数组的大小,即这个数组里能放几个元素。
初始化:
int a[num] = {初始化列表};
注:只有在定义的同时进行初始化才能这样写,如果已经定义完了,在用a[num] = {初始化列表}这样的形式是非法的。
初始化列表可以少于num,剩下的会自动补0;
这样的初始化可以省去num,即int a[ ] = {初始化列表};编译器会自动默认num位初始化列表的元素个数。
注意:num必须是能够确定的值,不能是未知的。如
scanf(“%d”,&num);
int a[num];是非法的,因为num的值无法确定,取决于输入。即C语言不能开辟动态大小的数组。(就是这个特点,对编程带来了多少的问题和困难啊!!我必须吐槽一下)
数组的访问:
采用数组名+下标的方式,如a[2],a[0],注意:数组内的元素是从0开始的,即a[0]代表第一个元素,所以在定义时的a[10]确实是说明它有10个元素,但,在使用是只能最大用到a[9],a[10]是非法的,因为它超出数组定义时的范围了。
我们现在已经知道如何使用数组中的元素了,a[n]就是数组中第n+1个元素,可以执行所有变量允许的操作,加减乘除都不在话下。
数组初始化
我们除了可以在定义的时候就赋初值,我们还可以在定义后赋值,不过这样无法使用大括号的方式,我们需要对每个数组元素进行操作,挨个给他们赋值,这是一个循环重复的操作,我们用循环结构完成
for(i=0;i<len;i++)
{
a[i] = num;
}
其中len是数组长度,注意:这里和数组定义时的数字是一样的,都表示数组内元素个数,例如int a[10],那么这个len就为10,因为当i=10的时候,不满足i<10,所以i取值为0到9,正好覆盖所有数组元素。
num是给数组赋值的元素,当然每次都可以是不一样的。
- 二维数组
对一维数组内的细分,假设数组名是房子,一维数组是各个房间,那么对一维数组再细分就能得到二维数组,如每个房间里的人就能构成一个二维数组,当我们希望查看某个人的信息时,我们先要找对房子,然后找见房间,再从这个房间中查看这个人而不是其他人的信息,着就时一个二维数组的访问过程。
先说定义
double b[num1][num2];
与一维同样的,double是这个二维数组里元素的类型,b是数组名,num1,num2是维度,如果把二维数组想成矩阵,分别是矩阵的行和列,想成矩形平面,分别是宽和长。
如:double b[5][2];表示这是一个5行2列的二维数组。按上面的理解,这是对一个元素个数为5的一维数组的再细分,每个元素再细分为2个元素,即可以看成是double b[5]的一个一维数组,每个元素包含2个值(或者说又包含一个长度为2的一维数组,两个一维一和,就是二维了。)(这种理解对维数扩展很重要,对指针的使用也有帮助。)
如何访问二维数组的元素呢?
一维一维的访问,如double b[2][2],先看第一维,表示咱们要访问第3个元素,由于第三个元素又是个一维数组,我们还要用下标的方式访问,即后面的数,表明我们要访问第三个元素(一个一维数组)中的第三个元素(是个double类型的变量)。注意,这里和一维是一样的,从0开始,最后一个数是长度减一。
初始化
同样的,可以在定义时就初始化,
Int a[ 3 ][ 4]={ { },{ },{ } };
这种形式再配合前面的讲解就很好理解了,二维是一维套一维,初始化就是两层大括号嵌套。
当然,也可以对每个第二维的元素的部分赋初值,其余的此维会自动补0
- 字符数组
当然字符数组只是类型发生了变化,结构和上面一二维是一样的。
Char a[10];
字符数组每个元素都是一个字符。其初始化的方式和上面讲的没有区别。访问也是一样的。
应当说明的是,C语言中对字符串的操作时通过字符数组来实现的。由于字符串会有结束标志‘\0’,所以字符数组中就认为出现‘\0’就是这个字符串的结束标志。
如字符串:char a[]={“HAPPY”};
那么a的长度是多少,答案是6,因为除了前面5个字母,还有一个结束标志\0,通常我们也可以将大括号省略。char a[]=“HAPPY”;系统会自动认出这是一个字符串,在结尾加结束标志‘\0’。
字符串的输出,可是使用%s的形式,如printf(“%s”,a)其中a为放这个字符串的数组名。输出时遇到第一个结束标志就结束输出。
字符串输入,同样的,scanf(“%s”,a)其中a要是已定义的字符数组,长度要大于输入的字符串的长度。
输入多个字符串,如scanf(“%s%s%s”,a,b,c),每个字符串之间用空格隔开。
对于字符串操作,我们有一些现成的方法,这些东西也不是C语言本身提供的,要包含说明书string.h。
puts(a) | 将a中的内容输出,a要为字符数组名 |
---|---|
gets(a) | 输入一个字符串放a中 |
strcat(a,b) | 把b字符串的内容拼接到a的后面,结果放在a字符串中,同时返回a的地址(这里的返回,地址的概念现在不用管,因为马上就会有了)注意,a要足够大,能放下两个拼接后的新字符串。 |
strcpy(a,b) | 把b中的内容复制到a去。同样的a要足够大。 |
strncp(a,b,n) | 把b中的前n个字符复制到a去 |
strcmp(a,b) | 将a与b的字符串比较,相同的话,返回0. |
strlen(a) | 测a的长度(不算结束符’\0’) |
strlwr(a) | a中大写变小写 |
strupr(a) | a中小写变大写 |
代码训练与详解
例:实现一个二维矩阵的转置。
#include<stdio.h>
int main()
{
int a[3][5]={{1,2,3,4,5},{5,6,7,8,9},{9,10,11,12,13}};
int i,j;
for(j=0;j<5;j++)
{
for(i=0;i<3;i++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
return 0;
}
转置就是行列互换,可以这样理解:原本是列满了,才进下一行,现在我们只有反过来,行满进下一列,就能实现转置的效果。
这里i,j表示原来的行列。
对于二维数组遍历时,通常都会用到2层循环,这有体现了循环的重要性。
结果:
这里每输出一行进行一个换行,可以使输出更为美观。
例:简单密码登录系统
分析:我们通过输入的密码与存储的密码相比较,等于的话输出成功登录,不等的话提示错误重新输入,错误超过5次结束程序。
#include<stdio.h>
#include<string.h>
int main()
{
char password[] = "123456";
char input[10] = {0};
int i=0;
while(i<5)
{
printf("Input the password:");
scanf("%s",input);
if(!strcmp(password,input))
{
printf("Right!\n");
break;
}else{
printf("Error!Try again!\n\a");
i++;
}
}
if(i == 5)
{
printf("Failed\a");
}
return 0;
}
首先我们假设密码为123456,用i记录错误次数,用input记录输入。
这里:if(!strcmp(password,input)),我们已经知道当这两个一样的时候,strcmp的结果会是0,其他时候不为0,我们这里希望判断它是否相等,相等则进入if语句,即strcmp的结果是0的时候进入,那么我们对它取反,就能顺利进入if语句,而在其他情况不为0,取反之后就会变为0,无法进入if语句,这正是我们想要的。
如果成功,我们就跳出,失败,我们就记录一次,从头再来。
注意这里判断失败的方式,5次机会,则i从0到4,当i等于5 时,我们让循环结束(因为没必要了)然后输出失败就行了,那么这里为什么要一个判断呢?因为如果有成功,就会跳出循环,如果不加限制,跳出循环一定会显示失败,这个程序不就是自己打架吗。所以我们加一定的限制。我们知道当循环因为失误太多而跳出时,i的值一定是5,而成功跳出,i的值一定小于5,这就成为一个判断的条件。
大家可以发现这里其实有个小bug,最后一次输入错误的话仍会提示再输入,这样的效果不是我们想要的,大家可以想想怎么改进一下。
C语言的一些基本概念都已经讲的差不多了,现在这些知识也足够编写一些像样的程序了。
后续内容就是真正C语言入门的知识,包括函数、指针、结构体、文件操作,每一章都极为重要。
我们下一讲再见!