C初阶- 02分支与循环语句

我决定了要成为海贼王便要为此而战,我必须变的更强。———路飞

阶段1目标:

基本了解C语言语法,站在全局,避去晦涩难懂,鲜明梳理C语言基本概念,为算法竞赛等计算机专业比赛铺好道路。传统功夫讲究点到为止,此阶段仅点明语法知识,后续阶段再进一步精进学习。

目录

分支与循环语句

1.分支语句

1.1if语句

1.2switch语句

2.循环语句

2.1while语句

2.2 for语句

2.3do...while循环

3.goto语句


分支与循环语句

本章重点学习分支语句和循环语句的使用。

C语言有3种结构:

  1. 顺序结构(一件事情从头做到尾)
  2. 选择结构(一件事情多种不同选择)
  3. 循环结构(一直在重复做一件事情)

1.分支语句

包括if语句,switch语句

1.1if语句

1.1.1if语句形式

if(表达式1){

      语句1;

}

else if(表达式2){

     语句2;

}

else{

    语句3;

}

首先看if()语句,如果满足表达式1,则进入if(表达式1){语句1},....以此类推,注意,程序在if分支语句中只会选择进入一个if

对比以下代码,可以看出,if(表达式)中的表达式不能连写,需要用 && 或者 | | 来表示

if(18 <= age < 30){

printf("青年\n");
}
//这是错误的代码段,不能连写表达式

说明:第二个程序会执行如下逻辑  —>  先从前往后算,18 <= 10,是非,用0表示,则0 < 30,是真,用1表示,则会打印出“青年”。

如果表达式的结果为真,则语句执行。

那么在C语言中如何表示真假? ——>  0表示假,非0表示

1.1.2if书写形式

当if语句想在同意表达式内执行多条代码的时候,要加大括号{  },这里的一对 {  } 称为一个代码块;否则容易出现歧义,代码对比如下:

1.1.3if语句代码风格

参考如下代码,试说出运行结果?

#include<stdio.h>
int main()
{
    int a = 0;
    int b = 2;
    if (a == 1)
        if (b == 2)
            printf("哈哈\n");
    else
            printf("呵呵");

    return 0;
}

——>结果为 空  ,在此程序中,我故意把else往前放,称为else悬空,营造和第一个if对其的假象,其实在if语句中,有条不成文的规定:else是和离他最近的if匹配的

另外,适当的添加{  }可以使代码逻辑更加清晰,即:上述代码等价于如下代码

由此稍稍浅谈C语言中的代码风格,养成良好的编码风格的重要性,例如:

//一些需要自己定义的变量,函数名一般需要直观的可以看出意义,例如:Add(10,20);

//自己定义变量名,一般小写单词之间用_连接,或者前一个单词第一个字母大写和后一个单词第一个字母大写,例如:
//char first_name[20];或者写作 char FirstName[20];

1.1.4if书写形式的对比

//代码1
if (condition) {
    return x; }
return y;

//代码2
if(condition) {
    return x; }
else
{
    return y; }

//代码3
int num = 1;
if(num == 5) {
    printf("hehe\n");
}

//代码4
int num = 1;
if(5 == num) {
    printf("hehe\n");

解释代码1:

碰到  return 代码执行结束,例如:

说明:if(1){ } 成立,进入表达式,打印出“呵呵”,之后碰到return 3程序结束,不再执行打印“哈哈”,return 是程序结束的标志

利用VS2019监视功能,ret接收test()的返回值,得出返回值为3

同理:if(0){  },返回值是-3

一般采用代码二,逻辑更加清晰

解释代码三,四:首先对比我给出的以下代码

//对比第一个第二个代码
//第一个
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int num = 3;
    if (num == 5) {
        printf("哈哈\n");
    }

    return 0;
}

//第二个
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int num = 3;
    if (num = 5) {
        printf("哈哈\n");
    }

    return 0;
}

注意:

没错,仅仅在if()判断里=数量不一样,首先第一段代码里判断运算if (num == 5),判断num和5是否相等,由程序知道num等于3,不等于5,所以不打印,为空,这是我们的本意;

可是当我们疏忽,漏泄一个等号,写成赋值运算if (num = 5),此时程序不会报错,会正常编译,进入分支语句,打印出“哈哈”,然而这不是我们的本意

而我们不想让这种错误发生,想出现这种情况时编译器会告诉我们从而避免错误,此时代码四的写法就应运而生了...

当写成赋值运算的时候,编译器会报错,从而提醒我们,因为赋值运算中变量不能赋值给常量

好的代码素养:

  1. 代码块能用尽量用,逻辑会更加清晰。
  2. 常量和变量比较相等的时候,提倡代码应该把常量放在左边更好一点,这样可以把本想写==判断语句而写成=赋值语句的情况避免掉。

一般程序中我们会采用代码二和代码四

1.2switch语句

switch语句也是一种分支语句。 常常用于多分支的情况。
比如:
输入 1 ,输出星期一
输入 2 ,输出星期二
输入 3 ,输出星期三
输入 4 ,输出星期四
输入 5 ,输出星期五
输入 6 ,输出星期六
输入 7 ,输出星期七
说明:当然我们可以采用if语句 if...else if ...else if来描述这段代码,但是这种代码略显冗余,这时候 switch语句就应运而生了。

1.2.1switch语句的形式

switch ( 整型表达式 )
{
    语句项;
}
语句项 是什么呢?
//是一些case语句
//如下:
case 整形常量表达式 :
    语句 ;
1.2.2break语句
case 语句是入口,break是出口
对比以下代码段:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {

	//代码显得冗长
	/*int day = 0;
	scanf("%d", &day);
	if (1 == day) {
		printf("星期一\n");
	}
	else if (2 == day) {
		printf("星期二\n");
	}
	else if (3 == day) {
		printf("星期三\n");
	}
	else if (4 == day) {
		printf("星期四\n");
	}
	else if (5 == day) {
		printf("星期五\n");
	}
	else if (6 == day) {
		printf("星期六\n");
	}
	else if (7 == day) {
		printf("星期七\n");
	}*/

	//——>采用switch语句
	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");
	}
}

代码解释:由以上代码片段,我们可以看出case 数字:——>仅仅是程序的入口,所以在输入4的时候,不单单输出“星期四”,而把case 4:往后的都打印出来了,那么如何达到我们的想要的输出效果呢?

这时就要我们引入关键字 break ,代码如下:

1.2.3default子句

思考一点,如果表达的值与所有的 case 标签的值都不匹配怎么办? 其实也没什么,程序并不会终止也不会报错,就是所有的语句都被跳过了而已。
然而我们不想让他直接跳过程序了,我们想让他输入错误的时候有个提示,告诉他输入错误,此时我们的defult语句就应运而生了
#define _CRT_SECURE_NO_WARNINGS
#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("输入数据有误,请输入1~7的数字:");
	}
}

注意:

switch表达式的值与所有case 标签的值都不匹配时,这个 default 子句后面的语句才会执行,所以,每个switch 语句中只能出现一条default子句
它可以出现在语句列表的任何位置

总结:

  1. switch(整型表达式),注意是整型表达式 int,见代码图1:
  2. case后面记得先加空格,再写整型常量表达式
  3. case后面必须加的是整型常量表达式,注意是整型  常量表达式,见代码图2:
  4. 整型常量表达式不单单指的是 整型数字1,2,3...还有可以转化为ASCII码值的字符例如‘a’,'b'...,见图3:
  5. switch语句中,break是一个case的出口,没有break,我们无法直接实现分支,搭配break使用才能实现真正的分支
  6. 当然,程序也不是有入口case就一定要有出口break,当几个连续发生的内容时,可以只有入口,没有出口,见图4:
  7. switch语句中运行程序最后的break可以加也可以不加,好的代码习惯应该加上,因为这样方便以后继续添加新的代码段,同见图4:
  8. switch表达式的值与所有case标签的值都不匹配时,这个default子句后面的语句才会执行,所以,每个switch语句中只能出现一条default子句,见图5:
  9. default字句可以出现在语句列表的任何位置,见图6:

图1

 图2

 图3

图4

图5

图6

注意点:switch语句可以嵌套使用

2.循环语句

1.while

2.for

3.do...while

好,由分支语句的if语句我们了解到,如下代码段:

if(条件){

     语句;

}

——>当条件满足的情况下, if 语句后的语句执行,否则不执行。但是这个语句只会执行一次。
但是我们发现生活中很多的实际的例子是:同一件事情我们需要完成很多次
那我们怎么做呢? C 语言中给我们引入了: while 语句,可以实现循环

2.1while语句

while(表达式)
{
    循环语句;
}
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
	int i = 1;
	while (i <= 10) {
		printf("%d\n",i);
		i++;
	}
	return 0;
}

此代码实现从1打印到10

那么下面我们来看一下while语句的执行流程图:

解释:while语句中会包含break和continue两个关键字,在特殊情况下才使用,那么下面我们来浅谈一下这两个关键字:

2.2.1while 语句中的 break
思考如下程序,会打印什么结果呢?
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
	int i = 1;
	while (i <= 10) 
	{
		if (5 == i)
			break;

		printf("%d\n", i);
		i++;
	}
	return 0;
}

说明:while里的break语句,满足if直接跳出while循环,即:无论是在while循环语句还是case分支语句,只要执行break都会终止后面的所有程序,跳出循环

即:在while 中的break是用于 永久 终止循环的。

2.2.2while语句中的continue

思考如下程序,会打印什么结果呢?

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
	int i = 1;
	while (i <= 10) 
	{
		if (5 == i)
			continue;

		printf("%d\n", i);
		i++;
	}
	return 0;
}

说明:continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分

解释:程序会输出1~4,当i=5时,此程序会执行continue,执行continue跳转到while(i<=10)位置,由于未执行i++,所以i恒为5,所以输出1~4以后程序是死循环才会有光标跳动

下面我用VS2019调试功能来监视i的变换情况:

说明:continue是用于跳过本次循环的,也就是本次循环中continue后边的代码不会再执行而是直接跳转到while语句的判断部分

同理,思考一下代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main() {
	int i = 1;
	while (i <= 10) 
	{
		i++;
		if (5 == i)
			continue;

		printf("%d\n", i);
		
	}
	return 0;
}

这里只给出结果:2,3,4,6,7,8,9,10  注意没有打印5

延伸一下:getchar()putchar(),首先给出如下代码:

解释说明:

getchar()——>从键盘获取一个字符

putchar()——>把字符打印在屏幕上

//其实如下代码等价
//1段
int ch = 0;
ch = getchar();
putchar(ch);
//


//2段
int ch = 0;
scanf("%s",&ch);
printf("%s",ch);
//

那么我们来思考一下如下代码段:

//代码什么意思?
//代码1
#include <stdio.h>
int main()
{
 int ch = 0;
 while ((ch = getchar()) != EOF)
       putchar(ch);
    return 0; 
}


//代码2
#include <stdio.h>
int main()
{
	int ch = 0;
	while ((ch = getchar()) != EOF)
	{
		if (ch < '0' || ch > '9')
			continue;
		putchar(ch);
	}
	return 0;
}

首先看代码1,我们得出如下运行结果:

是输入EOF才能让它停下来吗?好,我们试试看

可见输入EOF程序控制台还是没有让我们停止输入,那么我们不禁会想,程序中的(ch = getchar()) != EOF,是什么意思呢?

给出解释:EOF——end of file,是文件结束的标志,getchar()读取失败的时候就会返回EOF,而我们会在不想输入的时候按下Ctrl+Z,见下图:

代码解释:当我们按下Ctrl+Z的时候,getchar()返回EOF,那么程序中的(ch = getchar()) != EOF ——> EOF != EOF是假,即:不满足while循环条件,跳出循环,程序结束

你是不是以为这样代码1就结束了?不!难道你就没有疑问为什么  getchar()获取字符,为啥要用int ch = 0这种整型来定义呢?不应该是用char 类型来定义吗?我写错了??

哈哈哈,听我娓娓道来叭~

int整型类型定义而不用char类型定义原因主要有两点:

  1. 返回的是字符,字符本质上也是ASCII码值,是整数,用int类型没毛病。
  2. getchar函数不仅仅返回正常字符,还会返回EOF——而EOF本质上是-1(见下图),所以放在整型变量中,而不能放在char类型中。

下图说明EOF的本质定义为啥是-1:

1.选中EOF,鼠标右击,弹出窗口后点击转到定义。

2.我们可以看到EOF定义,就是-1。

那么我们的getchar()有啥用处吗?在啥场景下使用呢?下面给出一段代码,请思考我们会在输入密码出现什么结果?自己输入Y/N然后程序给你输出“确认成功!” 或 “确认失败!”吗??:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int ch = 0;
	char password[20] = { 0 };
	printf("请输入密码:");
	scanf("%s", &password);

	printf("请确认密码(Y/N):");
	ch = getchar();
	if (ch == 'Y')
	{
		printf("确认成功!");
	}
	else
	{
		printf("确认失败!");
	}
	return 0;
}

事与愿违,我们看出,输入一串密码后并没有给我任何输入Y/N的机会,直接跳出了“ 确认失败! ”,为啥?  

来来来,下面我们再再延伸普及一个知识——>输入缓冲区

来来来,说道说道为啥一点输入的机会都不给就给我们输出了“确认失败!”字样:

在此程序中,scanf输入12345,我们敲下回车\n,而此时会把12345传递给password,输入缓冲区只剩下我们敲的回车键\n,所以getchar捕获到了\n,发现不符合if(ch = Y),就默认执行了else语句,直接输出了“确认失败!”

注意点:

那么我们如何让程序老老实实运行呢?我们由上述可以得出,getchar函数可以提取输入缓冲区的字符,好!那么我们就利用一下getchar函数,让它替我们把\n先拿走

那么下一个getchar就啥字符也提取不到,就会等待用户输入Y/N.

你以为这就完事大吉,完事了??不不不,我再给出刚刚同样的代码,看看我这样输入是否还是能正常运行? ——> 12345 abcd

输入12345 abcd后,直接就跳转输出“ 确认失败!”,为什么呢?下面先普及一个知识点:

  1. 使用字符说明符%s时,一旦遇到空格,读取工作将终止
  2. getchar只能读取一个字符

看下面输入缓冲区执行流程:

解释说明:

键盘输入12345 abcd\n,scanf中的%s只能读取到不是空格的字符串,遇见空格就会读取停止,所以把12345读取到,拿给password

此时缓冲区还剩 abcd第一个getchar读取一个字符——>空格第二个getchar读取一个个字符——>a,然而a不等于Y,所以执行else语句,直接打印出“ 确认失败!”

所以我们得出结论,遇见空格这种在程序中插入getchar函数的方式去清理字符,清理不干净,没有普适性不可取,我们做出如下修改:

//清理\n
	//getchar();不可取
	while ((ch = getchar()) != '\n')
	{
		;
	}

解释说明:

我们发现直接用getchar清理字符清理不干净,我们就把代码改为如上图所示,先把键盘输入的12345拿给scanf,然后 abcd,传递给while循环,例如:

空格 != ' \n ',则进入while空循环内,把缓冲区的空格拿走清理掉了

a != ' \n ',则进入while空循环内,把缓冲区的a拿走清理掉了

...

如此循环执行,直到遇见\n,才不执行while循环,进入下一句程序,打印出“ 请确认密码(Y/N):”

上个问题基本结束讲解,再看代码二,会打印出什么呢?

//代码2
#include <stdio.h>
int main()
{
	int ch = 0;
	while ((ch = getchar()) != EOF)
	{
		if (ch < '0' || ch > '9')
			continue;
		putchar(ch);
	}
	return 0;
}

猜想:会打印ASCII码表里字符‘ 0 ’到‘ 9 ’的字符,下面附上ASCII表

解释说明:???

2.2 for语句

2.2.1for语句语法
我们已经知道了 while 循环,但是我们为什么还要一个 for 循环呢? 首先来看看 for 循环的语法:
for(表达式1;表达式2;表达式3)
 循环语句;

好,那么我们说说为啥我们在有了while循环的基础上,还要引入for循环的原因,先看下面代码:

若是需要在while中补充内容,需要空出那么多空,则while显得不太灵活,才引出for循环

那么写成for循环,代码如下:

对应的,for(表达式1;表达式2;表达式3){    循环体    } 中

表达式1:初始化

表达式2:判断

表达式3:调整

for执行流程图

好,看了流程图之后我们再讲讲for循环里的break语句和continue语句

2.2.2for循环中的break语句

首先给出如下代码,试试能否猜出打印结果:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int i = 1;
	for (i = 1; i <= 10; i++)
	{
		if (5 == i)
			break;
		printf("%d ", i);
	}
	
	return 0;
}

没错,打印结果是1 2 3 4,那么break语句在for循环里是什么作用呢?

break语句在for循环中的作用依然和while循环里的作用一样,都是用来终止程序

即:一碰到break语句,就跳出循环

2.2.3for循环中的continue语句

那么由break代码,可以知道break是用来终止循环的,那么continue语句是用来干什么的呢?

我们先看下列代码,试说出他的打印结果:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int i = 1;
	for (i = 1; i <= 10; i++)
	{
		if (5 == i)
			continue;
		printf("%d ", i);
	}
	
	return 0;
}

没错,打印结果是1 2 3 4 6 7 8 9 10,那么continue语句在for循环里是什么作用呢?

注意:在for循环中的continue语句不同于while循环中的continue语句,那么我们来对比一下他们的不同作用:

1.在for循环中,continue跳过continue后面的代码,去了循环中的调试部分,调整循环变量,不容易造成死循环。

2.在while循环中,continue跳过continue后面的代码,去了循环中的判断部分,容易死循环。

2.2.4for 语句的循环控制变量
一些建议如下:
1. 不可在for 循环体内修改循环变量,防止 for 循环失去控制,见代码1,图1:
2. 建议 for 语句的循环控制变量的取值采用 前闭后开区间 写法,见代码2,图2:
//代码1
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int i = 1;
	for (i = 1; i <= 10; i++)
	{
		if (i = 5)
		printf("%d ", i);
	}
	
	return 0;
}


//代码2
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	for (i = 0; i < 10; i++)//等价于for (i = 0; i <= 9; i++)
	{
		scanf("%d", &arr[i]);
	}
	
	for (i = 0; i < 10; i++)//等价于for (i = 0; i <= 9; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

图1

图2:

解释说明:
代码1:用赋值符号=,则if表达式非0,恒为真,所以一直死循环打印5

代码2:i < 10意思等价于i <= 9,但是i < 10有意义,表示0~9这十个元素,因此不采用i <= 9这种写法

2.2.5for 语句的省略问题
看下列程序会打印什么?:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	//省略问题
	//1.允许都省略
	//2.判断部分只要省略了,就意味着判断恒为“真”————>程序会死循环
	int i = 0;
	for (; ; )             //初始化,判断,调试,三个部分都省略了
	{
		printf("哈哈哈\n");
	}
	
	return 0;
}

解释说明:

结果是死循环打印“哈哈哈”

总结:省略问题
    1.允许都省略
    2.判断部分只要省略了,就意味着判断恒为“真”————>程序会死循环

注意,for循环体内我们尽量不要省略,以下代码对比说明:
//代码1
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	for (i = 0;i < 10 ;i++ )
	{
		for (j = 0; j < 10; j++)
		{
			printf("哈哈哈\n");
		}
	}
	
	return 0;
}

//代码2
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	for (;i < 10 ;i++ )
	{
		for (; j < 10; j++)
		{
			printf("哈哈哈\n");
		}
	}
	
	return 0;
}

先给结果:代码1,结果是打印出100行“哈哈哈”;代码2,结果是打印10行“哈哈哈”

解释说明:
代码1:i = 0满足i < 10,进入第一个for循环,接着进入第二个for循环,j = 0,满足j < 10,执行打印第一行“哈哈哈”;接着j++,即j = 1,继续执行第二个for循环,j = 1,满足j < 10,打印第二行“哈哈哈”,...,直到j = 10的时候,退出第二个for循环,回到第一个for循环,以上是i = 0的时候打印情况,打印了十行“哈哈哈”;       接着i++,即i = 1,i = 1满足i < 10,进入第一个for循环内,接着又进入第二个for循环,继续执行刚才的操作...
代码2:第一个for循环,初始化为空,默认上面的int i = 0这个初始化,i = 0 满足i < 10,进入第一个for循环,接着进入第二个for循环,同理,第二个for循环初始化也默认是int j = 0,j = 0 满足j < 10,打印第一行“哈哈哈”;接着j++,即使j = 1,继续执行第二个for循环,j = 1,满足j < 10,打印第二行“哈哈哈”,...,直到 j = 10的时候,退出第二个for循环,注意此时 j = 10 ,退出第二个for循环,以上是i = 0的时候打印情况,打印了十行“哈哈哈”;       接着i++,即i = 1,i = 1满足i < 10,进入第一个for循环内,接着又进入第二个for循环,注意,此时没有初始化部分,即: 没有j = 0部分让j重置,则j还是保持上一次循环的j = 10,即不在满足进入第二个for循环的条件,仅输出10行“哈哈哈”

2.3do...while循环

2.3.1do 语句的语法:
do
{
    循环语句;
}
while(表达式);
应用如下所示代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int i = 0;
	do 
	{
		printf("%d ", i);
		i++;

	} while (i <= 10);
	
	return 0;
}

do...while循环执行流程

2.3.2do...while循环里的break语句

先看一段代码,试说出执行结果:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int i = 0;
	do 
	{
		if (5 == i)
			break;
		printf("%d ", i);
		i++;

	} while (i <= 10);
	
	return 0;
}

没错,和while循环和for循环一样,结果会打印1 2 3 4,此处不带冗述break语句的语法,详细见while循环和for循环

2.3.3do...while循环里的continue语句

先看一段代码,试说出执行结果:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int i = 0;
	do 
	{
		if (5 == i)
			continue;
		printf("%d ", i);
		i++;

	} while (i <= 10);
	
	return 0;
}

是的,看完do...while的执行流程图再解决这个问题就知道,do...while循环里的continue语句和while循环里的continue语句一样,遇见了会发生死循环,如下图,光标闪烁

2.3.4do...while语句的特点

循环至少执行一次,使用的场景有限,所以不是经常使用。
哦吼,是不是跃跃欲试呢?下面选择合适的循环语句来做做练习题叭~~
1. 计算 n 的阶乘。
2. 计算 1!+2!+3!+……+10!
3. 在一个有序数组中查找具体的某个数字 n 。 编写 int binsearch(int x, int v[], int n); 功能:在 v[0]
<=v[1]<=v[2]<= ….<=v[n-1] 的数组中查找 x.
4. 编写代码,演示多个字符从两端移动,向中间汇聚。
5. 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则
提示登录成,如果三次均输入错误,则退出程序。
第一题
//第一题
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int a = 1;
	int b = 1;//阶乘
	int n = 0;
	scanf("%d", &n);
	for (a = 1; a <= n; a++)
	{
		b *= a;
	}
	printf("!n = %d", b);
	return 0;
}

第二题

//第二题
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int a = 1;
	int b = 1;//阶乘
	int sum = 0;
	for (a = 1; a <= 10; a++)
	{
		
		b *= a;
		sum += b;
	}
	printf("1! + 2! + 3! +4!...+10! = %d", sum);
	return 0;
}

那么针对第二题,我们来讨论一个写法,是否可以对第一题代码进行改造,暴力解呢?

即:用第一题的方法计算阶乘,再改造一下,求出每个数字的阶乘的和

我们发现,这样稍稍修改代码好像说的过去,来运行试试,看看行不行?

(皮一下~.jpg)由于1!+2!+3!+...+10!数字较大,我们假设这个代码还需要进一步修缮,那么怎么修改呢?————不用着急,可以用调试功能

(程序是可以运行的,但是结果是错误的,这就叫做运行时错误,这个时候用调试功能,一步一步执行代码,观察代码是不是按照我们的期望走的,如果发现代码不是按照期望走的,bug就出现了!)

假设代码是从1!+2!+3!,那么程序会给我们正确结果9吗?

可见程序并没有给出正确结果,那么需要修改哪里呢?单步调试一下便知

经过调试,发现当n=3时出现问题,算出b是12,应该是6才对,那么问题出现在哪呢?

其实,是因为上一次算出的b累计的效果,那么我们把b每次初始化即可

第三题

//第三题
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	//0~9
	int arr[10] = { 1,2,3,4,6,12,16,18,23,90 };
	//只知道给了有序数组arr[],不知道给了多少个,也不知道给了什么数的情况下
	//想实现在有序数组中,找数字6,找到则输出下标,找不到输出“找不到”

	//求数据的元素个数
	//                 40 / 4  
	int sz = sizeof(arr) / sizeof(arr[0]);

	int i = 0;
	int k = 6;
	for (i = 0; i < sz; i++)
	{
		if (arr[i] == k)
		{
			printf("找到了,下标是:%d", i);
			break;
		}

	}
	if (i == sz)
	{
		printf("找不到\n");
	}

	return 0;
}

技巧:求数据的元素个数(数组元素字节数/每个元素所占字节数)

 //                       40 / 4  
    int sz = sizeof(arr) / sizeof(arr[0]);

是的,我们这样可以找的数组中符合条件的数字下标,但是效率太低,需要从前往后逐一查找

由此我们引出一种算法——> 折半查找(二分法)

折半查找思想

折半查找,效率更高

//第三题优化(折半查找)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	//0~9
	int arr[10] = { 1,2,3,4,6,12,16,18,23,90 };
	//只知道给了有序数组arr[],不知道给了多少个,也不知道给了什么数的情况下
	//想实现在有序数组中,找数字6,找到则输出下标,找不到输出“找不到”

	//求数据的元素个数
	//                 40 / 4  
	int sz = sizeof(arr) / sizeof(arr[0]);

	int k = 6;//要找的数字
	int left = 0;
	int right = sz - 1;
	int mid = (left + right) / 2;
	while (left <= right)
	{
		if (arr[mid] > k)
		{
			right = mid - 1;
		}
		else if (arr[mid] < k)
		{
			left = mid + 1;
		}
		else
		{
			printf("找到了,下标是%d\n", mid);
			break;
		}

	}
	
	if (left > right)
	{
		printf("找不\n");
	}
	return 0;
}

第四题

题目先给出解释,编写代码,演示多个字符从两端移动,向中间汇聚。什么意思呢?

例如:我们要实现“hello world!!!!!”则按照题目意思应该变为如下打印过程:

	//hello world!!!!!!
	//#################
	//h###############!
	//he#############!!
	//hel###########!!!
	//hell#########!!!!
	// ...
	//hello world!!!!!!

那么代码如何实现呢?                            

//第四题
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
int main()
{
	//hello world!!!!!!
	//#################
	//h###############!
	//he#############!!
	//hel###########!!!
	//hell#########!!!!
	// ...
	//hello world!!!!!!
	char arr1[] = { "hello world!!!!!!" };
	char arr2[] = { "#################" };
	int len = strlen(arr1);//需要自行添加头文件——>#include<string.h>

	int left = 0;
	int right = len - 1;
	while (left <= right)
	{
		arr2[left] = arr1[left];
		arr2[right] = arr1[right];
		printf("%s\n", arr2);

		left++;
		right--;

	}
	
	return 0;
}

打印效果如下:

技巧:计算字符串的长度代码

int len = strlen(arr1);//arr1是数组名

注意点:strlen计算字符串长度,需要自行添加头文件#include<string.h>

那么我们想看看打印效果一行一行停顿输出怎么实现呢?

//第四题优化(一行一行输出)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
#include<windows.h>
int main()
{
	//hello world!!!!!!
	//#################
	//h###############!
	//he#############!!
	//hel###########!!!
	//hell#########!!!!
	// ...
	//hello world!!!!!!
	char arr1[] = { "hello world!!!!!!" };
	char arr2[] = { "#################" };
	int len = strlen(arr1);//需要自行添加头文件——>#include<string.h>

	int left = 0;
	int right = len - 1;
	while (left <= right)
	{
		arr2[left] = arr1[left];
		arr2[right] = arr1[right];
		printf("%s\n", arr2);
		Sleep(1000);//1000毫秒——>1s

		left++;
		right--;

	}
	
	return 0;
}

可见我们就增加了两行代码:

Sleep(1000);//1000毫秒——>1s

不要忘记增加这一句需要自行添加头文件#include<windows.h>

下面我们来看实现效果

那我们如果想一行打印以后清理上一行再打印下一行怎么实现呢?

system("cls");  ——>清理干净

注意需要自行添加头文件#include<stdlib.h>

实现效果代码如下:

//第四题优化(打印一行清理一行)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
#include<windows.h>
#include<stdlib.h>
int main()
{
	//hello world!!!!!!
	//#################
	//h###############!
	//he#############!!
	//hel###########!!!
	//hell#########!!!!
	// ...
	//hello world!!!!!!
	char arr1[] = { "hello world!!!!!!" };
	char arr2[] = { "#################" };
	int len = strlen(arr1);//需要自行添加头文件——>#include<string.h>

	int left = 0;
	int right = len - 1;
	while (left <= right)
	{
		arr2[left] = arr1[left];
		arr2[right] = arr1[right];
		printf("%s\n", arr2);
		Sleep(1000);//1000毫秒——>1s
		system("cls");
		left++;
		right--;

	}
	
	return 0;
}

第五题

自己运行一下看看如下代码是否正确?

//第五题
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int i = 0;
	char password[20] = { 0 };
	//假设密码是  abcdef

	for (i = 0; i < 3; i++)
	{
		printf("请输入密码:\n");
		scanf("%s",&password);
		if (password == "abcdef")
		{
			printf("密码输入正确!\n");
			break;
		}
		else
		{
			printf("密码输入错误!!");
		}
	}
	if (i == 3)
	{
		printf("三次密码输入失败,退出程序!!");
	}
	return 0;
}

我们运行后发现,如下代码并不正确,输入abcdef显示输入错误,什么原因呢??

是因为,两个字符串比较,不能用==表示,需要用另一个函数表示:

strcmp(password,"abcdef") == 0

注意:需要自行添加头文件#include<string.h>

第五题正确代码:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
int main()
{
	int i = 0;
	char password[20] = { 0 };
	//假设密码是  abcdef

	for (i = 0; i < 3; i++)
	{
		printf("请输入密码:\n");
		scanf("%s",&password);

		if (strcmp(password,"abcdef") == 0)
		{
			printf("密码输入正确!\n");
			break;
		}
		else
		{
			printf("密码输入错误!!");
		}
	}
	if (i == 3)
	{
		printf("三次密码输入失败,退出程序!!");
	}
	return 0;
}

3.goto语句

C 语言中提供了可以在一个函数中 随意跳转的 goto语句 ,从理论上 goto 语句是没有必要的,实践中没有 goto 语句也可以很容易的写出代码。    那么下面我们来看看goto语句如何应用的,再去分析何时用得到goto语句。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{

again:
	printf("哈哈哈\n");
	goto again;
	return 0;

}

结果会产生死循环,那么我们什么时候需要goto语句呢?

for(...)
    for(...)
   {
        for(...)
       {
            if(disaster)
                goto error;
       }
   }
    …
error:
 if(disaster)
         // 处理错误情况

如上述代码段,我们想要跳出多层深入嵌套循环,怎么办呢?用break?不,break只能跳出一层循环,那怎么办呢?——> 这个时候我们就采用goto语句跳转

下面我们来点刺激的——>关机程序

要求,程序运行起来就倒计时关机,1分钟内,输入:我是猪,就取消关机

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	char input[20] = { 0 };
	system("shutdown -s -t 60");

again:
	printf("请注意,你的电脑在1分钟内将会关机,如果输入:我是猪,将会取消关机\n");
	scanf("%s", &input);
	if (strcmp(input, "我是猪") == 0)
	{
		system("shutdown -a");
	}
	else
	{
		goto again;
	}

	return 0;

}

那么先来普及一个知识,再来解释代码叭~~

我们借用windows系统的命令提示符,来完成关机的代码

百度百科解释:

windows系统的命令提示符:Windows 命令提示符(即 cmd)是 Windows 系统的一种命令行操作工具,用户可以通过输入命令来完成各种各样的系统或程序操作。 虽然很多操作都可以通过图形程序完成,但也有非他不可的情况存在。 因此了解一些日常可能用到的简单操作也是很必要的。 ————>打开方式:

按下快捷键 Win + R 打开运行,输入 cmd 回车

按住Win+R,出现如下界面,然后输入cmd回车

之后再输入shutdown -s -t 60回车

之后按shutdown -a,取消关机

这就是windows系统的命令提示符,其实goto语句在这种不是多个循环中还可以改成while循环,用break跳出,代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	char input[20] = { 0 };
	system("shutdown -s -t 60");

	while (1)
	{
		printf("请注意,你的电脑在1分钟内将会关机,如果输入:我是猪,将会取消关机\n");
		scanf("%s", &input);
		if (strcmp(input, "我是猪") == 0)
		{
			system("shutdown -a");
			break;
		}
	}

	return 0;

}

 —— —— —— —— —— —— —— ~~~我是正文分割线~~~ —— —— —— —— —— —— —— 

恭喜你,坚持住,即将变秃变强,记得及时巩固复习哟~~~~

  • 15
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.阿Q.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值