C语言总结项目和入门——基础下

废话不多说,咱们马上开始。

三、C语言基础下(从交互到数组)

一、输入输出

  从上一章的例子可以看出,很多时候我们需要向程序输入一些数据,或者希望程序将一些数据以可视化的方式呈现在我们眼前。人机的交互就显得十分重要。
  C语言本身不提供输入输出的语句,但它提供了一个标准库,即说明书,里面定义了一些输入输出的方法,如printf和scanf。本章将介绍这些方法的用法。
  使用这些方法之前,要包含这个说明书哦(#include<stdio.h>)。

  1. 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个%%,这时才会输出一个%字符。

  1. 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语言怎么实现字符串的操作?是通过数组来实现的,这里先埋个伏笔。

二、程序结构

在这一章,我们来从整体程序的角度,看看程序一般由什么部分构成。
程序结构分三大块,选择结构、循环结构、顺序结构
我们前面写的都是顺序结构,即从头写到尾,这个结构是线性的。

  1. 选择结构

  还记得三目运算符吗,它通过判断逻辑表达式的真假,实现执行不同的代码的作用,这就是选择结构。选择结构通过判断,根据判断的结果执行不同的代码块。

  • 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(“闰年”);
}
  前面用来判断是否是百年。

  1. 循环结构

  当我们希望程序循环执行某一块程序,如循环输出,循环相加。我们要是使用顺序执行,就必须写很多行形式相同的代码,当然我们也可以使用循环结构来简化编程。
循环结构就是让程序循环执行一块程序。
  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语言入门的知识,包括函数、指针、结构体、文件操作,每一章都极为重要。
  我们下一讲再见!


  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值