C语言—分支和循环语句

C语言是结构化的程序设计语言。所谓的结构化一共抽象成三种结构:1.顺序结构 2.选择结构 3.循环结构

什么是语句

C语言中有一个分号;隔开的就是一条语句。

比如:

printf("hehe\n");
	3 + 5;
	; //空语句
	return 0;

这些都是语句。

分支语句(选择结构)

if语句

语法结构:
在这里插入图片描述

注意:C语言表示真和假是通过非0就是真,0就是假从而进行区分的。

总结

  • 单分支:if语句中的表达式如果表达式为非0即为真,运行if语句里的代码;表达式运算的是0就不执行if里面的语句。
  • 双分支:if语句中的表达式如果表达式为非0即为真,运行if语句里的代码(语句1),不运行else里的代码(语句2);表达式运算的是0就不执行if里面的语句(语句1),而执行else里的代码(语句2)。
  • 多分支:如果if语句中的表达式1和else if语句中表达式2 不成立,运行else语句 语句3的代码;如果if语句中的表达式1成立运行语句1中的代码,其他分支不执行;如果else if语句中的表达式2成立运行语句2中的代码,其他它分支不执行。

if语句的使用:
1.单分支

#include<stdio.h>
int main()
{
	int age = 10;
	if (age >= 18)
	{
		printf("成年\n");
	}
	return 0;
}

运行结果:
在这里插入图片描述
如果上面的age=20

在这里插入图片描述
注:if语句后面可以只跟一条语句,也可以不带else

2.双分支

#include<stdio.h>
int main()
{
	int age = 10;
	if (age >= 18)
	{
		printf("成年\n");
	}
	else
	{
		printf("未成年\n");
	}
	return 0;
}

在这里插入图片描述

if、else语句需要注意以下的使用:

#include<stdio.h>
int main()
{
	int age = 23;
	if (age >= 18)
	{
		printf("成年\n");
	}
	else
		printf("未成年\n");
		printf("不能谈恋爱\n");

	
	return 0;
}

代码写成这样运行结果:
在这里插入图片描述

这样写和下面的代码没有任何区别!

#include<stdio.h>
int main()
{
	int age = 23;
	if (age >= 18)
	{
		printf("成年\n");
	}
	else
		printf("未成年\n");

	printf("不能谈恋爱\n");  //跟上头的代码没区别!!!

	
	return 0;
}

注意:if 和else 默认只能控制一条语句,要想控制多条语句就要加上 {}

改进:
在这里插入图片描述
这里的一对{ }就是一个代码块。

在这里插入图片描述
3.多分支

#include<stdio.h>
int main()
{
	int age = 20;
	if (age < 18)
	{
		printf("少年\n");
	}
	else if(18<=age && age<26)
	{
		printf("青年\n");
	}
	else if (age >= 26 && age < 40)
	{
		printf("中年\n");
	}
	else if (age >= 40 && age < 60)
	{
		printf("壮年\n");
	}
	else if (age >= 60 && age <=100)
	{
		printf("老年\n");
	}
	else 
	{
		printf("老不死\n");
	}
	return 0;
}

运行结果:

在这里插入图片描述
注意:
1.
在这里插入图片描述
2. 多分支语句中else可以写也可以不写,视情况而定

悬空else

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

运行结果:
在这里插入图片描述
解释如下:
在这里插入图片描述

总结:

  • else跟离它最近的if匹配
  • 适当的使用{ }可以使代码的逻辑更加清楚,代码风格很重要

if 书写形式的对比

//代码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");
}

代码2和代码4更好,逻辑更加清晰,不容易出错。

练习

输出1-100之间的奇数(这里就写出其中一种代码)

#include<stdio.h>
int main()
{
	int i = 0;
	for (i = 1;i <= 100;i++)  //for(int i = 1;i <= 100;i++)这样的写法C++、C99是支持
	{
		if (i % 2 == 1)
		{
			printf("%d ", i);
		}
	}
	return 0;
}

在这里插入图片描述

switch语句

switch语句也是一种分支语句。常常用于多分支的情况。

switch的语法:

switch(整形表达式)
{
	case 整型常量表达式:
	语句;
}	

注意:case 整型常量表达式:中的整型常量表达式可以是1+2、1、2等等,不能是1.0、n、x 等等 这些变量。此外,字符也可以(字符也属于整型中的一种(字符底层存储用的是字符的ASCII值))

switch语句的使用:

#include<stdio.h>
int main()
{
	int day = 0;
	scanf("%d", &day);
	switch (day)
	{
	case 1:
		printf("星期1\n");
	case 2:
		printf("星期2\n");
	case 3:
		printf("星期3\n");
	case 4:
		printf("星期4\n");
	case 5:
		printf("星期5\n");
	case 6:
		printf("星期6\n");
	case 7:
		printf("星期天\n");
	}
	return 0;
}

在这里插入图片描述

解释:switch表达式的结果是几 ,它就从case后面对应的数进去(入口),执行到switch结束。如果想要实现分支,需要搭配break使用才能实现真正的分支。

在switch语句中的break

上段代码改正如下:

#include<stdio.h>
int main()
{
	int day = 0;
	scanf("%d", &day);
	switch (day)
	{
	case 1:
		printf("星期1\n");
		break;
	case 2:
		printf("星期2\n");
		break;
	case 3:
		printf("星期3\n");
		break;
	case 4:
		printf("星期4\n");
		break;
	case 5:
		printf("星期5\n");
		break;
	case 6:
		printf("星期6\n");
		break;
	case 7:
		printf("星期天\n");
		break;
	}
	return 0;
}

在这里插入图片描述
switch语句中 case是入口;break是出口。这样的搭配才能实现真正的分支

例如:
1-5 工作日
6-7 休息日

#include<stdio.h>
int main()
{
	int day = 0;
	scanf("%d", &day);
	switch (day)
	{
	case 1:	
	case 2:	
	case 3:
	case 4:
	case 5:
		printf("weekday\n");
		break;
	case 6:
	case 7:
		printf("weekend\n");
		break;
	}
	return 0;
}

在这里插入图片描述
注意:break语句的实际效果是把语句列表划分为不同的部分。break语句不是必须要在case语句中添加的,具体问题具体分析

编程好习惯

在最后一个case语句的后面加上一条break语句。(之所以这么写是可以避免出现在以前的最后一个case语句后面忘记添加break语句)。

default子句

如果switch语句中的表达的值与所有的case标签的值都不匹配时,这时就需要default子句。

例如上段代码如果输入 9的话运行结果什么都没有,因此可以优化为

#include<stdio.h>
int main()
{
	int day = 0;
	scanf("%d", &day);
	switch (day)
	{
	case 1:	
	case 2:	
	case 3:
	case 4:
	case 5:
		printf("weekday\n");
		break;
	case 6:
	case 7:
		printf("weekend\n");
		break;
	default:
		printf("输入错误\n");
		break;
	}
	return 0;
}

在这里插入图片描述
default子句:
1.可以写在任何一个case标签可以出现的位置;
2.switch语句中只能出现一条default子句
3.它可以出现在语句列表的任何位置

编程好习惯

在每个switch语句中都放一条default子句是个好习惯,甚至可以在后边再加一个break。

练习

#include<stdio.h>
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;
}


过程
在这里插入图片描述
注:break语句是跳出自身所在的switch语句

循环语句

  • while
  • for
  • do while

while循环

while语法结构:

while(表达式)
	循环语句结构

在这里插入图片描述

用法: 如果while(表达式)中的表达式成立,运行循环语句结构;否则,不运行。(while跟if语句类似)

例如:打印1-10的数字

#include<stdio.h>
int main()
{
	int i = 1;
	while (i <= 10)
	{
		printf("%d ", i);
		i++;
	}
	return 0;
}

在这里插入图片描述

while语句中的break和continue

break介绍

上段代码改写为:
在这里插入图片描述
总结:break在while循环中的作用其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。 所以while中的break是用于永久终止循环的。(在while中break用于永久的终止循环)

continue介绍

continue使用的实例:
在这里插入图片描述
此段代码进入死循环了

在这里插入图片描述
总结在while循环中,continue的作用是跳过本次循环 continue后面的代码,直接去判断部分,看是否进行下一次循环。

while循环的应用

代码一
不断地在屏幕上打印字符

#include<stdio.h>
int main()
{
	char ch = 0;
	//ctrl+z-getchar就读取结束
	while ((ch = getchar()) != EOF)
	{
		putchar(ch); //输出一个字符
	}

	return 0;
}

在这里插入图片描述

补充:

  • getchar函数从标准输入获取字符。返回值是该字符的ascll值,否则返回EOF。
  • putchar函数在标准输出中输出字符。返回值写入成功返回该字符的ascll值返回,否则返回EOF
  • 在这里插入图片描述
    EOF - end of file - 文件的结束标志
  • ctrl+z代表着getchar读到EOF,读取结束
  • getchar函数也会读取\n(getchar认为缓冲区中的任何一个字符都是字符包括\n)

代码二
getchar的使用场景

#include<stdio.h>
int main()
{
	char password[20] = { 0 };
	printf("请输入密码:>");
	scanf("%s", password);  //password没取地址是因为password本身是个数组,数组的数组名本身就是个地址
	printf("请确认密码(Y\N):>");
	int ch = getchar();
	if (ch == 'Y')
	{
		printf("确认成功\n");
	}
	else
	{
		printf("确认失败\n");
	}
	return 0;
}

运行结果:
在这里插入图片描述

运行结果和优化过程(包含讲解)
在这里插入图片描述
最终代码:

#include<stdio.h>
int main()
{
	char password[20] = { 0 };
	printf("请输入密码:>");
	scanf("%s", password);
	printf("请确认密码(Y/N):>");
	//清理缓冲区去
	//getchar();//处理 \n
	//处理缓冲区中的多个字符
	int tmp = 0;
	while ((tmp = getchar()) != '\n')
	{
		;
	}
	int ch = getchar();
	if (ch == 'Y')
	{
		printf("确认成功\n");
	}
	else
	{
		printf("确认失败\n");
	}
	return 0;
}
	

总结:

  • getchar、scanf函数都是输入函数,是从缓冲区中拿数据的并不是从键盘上拿数据的
  • \n会触发scanf函数从缓冲区中读取内容,scanf函数并不会读取\n或者空格,而getchar函数会读取\n或者空格
  • getchar函数读取的是字符返回的是整型是因为getchar读取的是字符而字符本质上是以ASCII值(ASCII值是整型)的形式存储的;getchar函数读取的时候不一定返回的是ASCII值有可能返回EOF(EOF为-1)是整型

代码三

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

在这里插入图片描述
本段代码只打印数字

for循环

语法

for(表达式1,表达式2,表达式3)
	循环语句

表达式1为初始化部分,用于初始化循环变量的;表达式2为条件判断部分,用于判断循环什么时侯终止;表达式3为调整部分,用于循环条件的调整。

举个例子:打印1-10

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

在这里插入图片描述
对比一下for和while循环
在这里插入图片描述
总结: 可以发现while循环中依然存在循环的三个必须条件,但是由于风格的问题使得三个部分很可能偏离较远,这样查找修改就不够集中和方便。因此for循环的风格更胜一筹,使用频率较高。

在这里插入图片描述

break和continue在for循环中

for循环中的break和continue,它们的意义和while循环中的类似但是还是有些差异
在这里插入图片描述
for循环中的break语句用于终止整个循环的

对比 1
在这里插入图片描述
对比 2
在这里插入图片描述
注:

  • for循环中的continue语句是跳过本次循环continue后面的代码,一旦跳过这段代码之后紧接着就要执行for循环中调整部分中的语句
  • while循环中的continue和for循环中的continue是有所区别的:for循环中的continue它跳到调整部分;而while循环中的continue它有可能把调整部分跳过去导致程序死循环

在这里插入图片描述

总结:for循环和while循环中的break使用上是一样的;然而在有的时候例如(对比2)continue在for和while循环中的使用,运行结果就不同

for语句的循环控制变量

建议:

1.不可在for循环体内修改循环变量,防止for循环失去控制。

2.建议for语句的循环控制变量的取值采用“前闭后开区间”写法。

一些for循环的变种

变种1.(1)

#include<stdio.h>
int main()
{
	//判断部分的省略-判断部分恒为真
	for (;;)
	{
		printf("hehe\n");
	}
	return 0;
}

在这里插入图片描述
注:

  • for循环中,初始化部分、判断部分、调整部分都可以省略
  • 判断部分的省略,判断部分恒为真,程序可能进入死循环

变种1.(2)
在这里插入图片描述
只会打印i=0时的三次hehe。因为内循环中没写初始化默认使用外循环外的j变量,当j=3时外循环无论循环多少次,内循环依旧不满足判断条件不会进入内循环,从而只会打印三次hehe

变种2:
在这里插入图片描述

注:for循环中可能存在多个循环变量控制循环

练习

请问循环要循环多少次

#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是赋值不是判断)

过程
在这里插入图片描述

do…while()循环

do…whille()语句的语法:

do
	循环语句;
while(表达式);

执行流程:无论条件是否成立都先执行一次循环语句;接下来如果条件成立执行循环语句;否则不执行。

例如 :打印数字1-10
在这里插入图片描述
执行流程:
在这里插入图片描述

do while循环中的break和continue

在这里插入图片描述
在这里插入图片描述
do while语句的特点:循环至少执行一次,使用的场景有限,所以不是经常使用。

总结:do……while()循环和while循环使用方法类似不过do……while()循环无论判断条件如何都会至少执行一次。这两个循环中break语句和continue语句的使用方法一样

练习

1.计算n的阶乘

#include<stdio.h>
int main()
{
	int n = 0;
	int i = 0;
	int ret = 1;
	scanf("%d", &n);
	for (i = 1;i <= n;i++)
	{
		ret *= i;
	}
	printf("%d", ret);
	return 0;
}

在这里插入图片描述
2.计算1!+2!+…n!

#include<stdio.h>
int main()
{
	int n = 0;
	int i = 0;
	int ret = 1;
	int sum = 0;
	scanf("%d", &n);
	for (i = 1;i <= n;i++)
	{
		ret *= i;
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;
}

在这里插入图片描述

3.在一个有序数组中查找具体的某个数字n(本题用到的是折半查找(二分查找)算法

思想:
在这里插入图片描述

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int k = 7;//要查找的数字
	//在arr这个有序数组中查找k=7的值
	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)
		{
			left = mid + 1;
		}
		else if (arr[mid] > k)
		{
			right = mid - 1;
		}
		else
		{
			printf("找到了,下标是:%d\n", mid);
			break;
		}
	}
	if (left > right)
	{
		printf("找不到了\n");
	}
	return 0;
}

在这里插入图片描述

每一次找被查找范围的中间元素的方法,查一次去掉一半数据这种方法——折半查找(二分查找)。时间复杂度为O(logN)

4.编写代码,演示多个字符从两端移动,向中间汇聚。

#include<stdio.h>
#include<string.h>
#include<windows.h>
int main()
{
	char arr1[] = "welcome to my family !!!!!!";
	char arr2[] = "###########################";
	int left = 0;
	int right = strlen(arr1) - 1;
	while (left <= right)
	{
		arr2[left] = arr1[left];
		arr2[right] = arr1[right];
		printf("%s\n", arr2);
		Sleep(1000); //休眠一秒钟
		system("cls"); //清空屏幕
		left++;
		right--;
	}
	printf("%s\n", arr2);
	

	return 0;
}

在这里插入图片描述
5.编写代码实现,模拟用户登录情景,并且只能登陆三次

#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, "123456") == 0)
		{
			printf("登陆成功\n");
			break;
		}
		else
			printf("密码错误,请重新输入!\n");
	}
	if (i == 3)
	{
		printf("三次密码均错误,退出程序\n");
	}
	return 0;
}

在这里插入图片描述
注:

  • 两个字符串比较不能使用==去比较,应该使用strcmp函数去比较
  • strcmp函数用于比较两个字符串的大小,头文件string.h,返回值如果两个字符串相等返回0;如果第一个字符串大于第二个字符串返回>0;如果第一个字符串小于第二个字符串返回<0;
  • 两个字符串比较大小比较的是ASCII值不是字符串长度
  • 上述代码中password==“123456”,比较的不是字符串的内容而是两个字符串的首字符的地址

goto语句

使用场景

C语言中提供了可以随意滥用的goto语句和标记跳转的标号。
从理论上goto语句是没有必要的,实践中没有goto语句也可以很容易地写出代码。
但是,某些场合下goto语句还是用得着的,常见的用法就是终止程序在某些深度嵌套的结构的处理过程,例如一次跳出两层或多层循环。
这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。

goto语句的语法

1
label: statement;
goto label;
2``
goto label;
label: statement;
以上是goto语句的两种基本用法。
例如

#include<stdio.h>
int main()
{
flag:
	printf("haha\n");
	printf("hehe\n");
	goto flag;
	return 0;
}

注:goto语句会打乱程序正常执行的流程

实例:一个关机程序

要求:
关机程序
只要运行起来,电脑就在一分钟内关机,如果输入我是猪,就取消关机。

原码
代码一

//关机程序
//只要运行起来,电脑就在一分钟内关机,如果输入我是猪,就取消关机。
//shutdowm -s -t 60   设置在60s之后关机
//shutdown -a    取消关机    
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	char input[20] = { 0 };
	//关机
	//C语言提供了一个函数:system()-执行系统命令的
	system("shutdown -s -t 60");
again:
	printf("请注意,你的电脑在1分钟内关机,如果输入:我是猪,就取消关机\n");
	scanf("%s", input);
	if (strcmp(input, "我是猪") == 0) 
	//两个字符串比较是不能使用==的,应该使用strcmp() string compare
	{
		system("shutdown -a");
	}
	else
	{
		goto again;
	}

	return 0;
}

这个运行结果就不显示了。

代码二

#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;
}

以上两段代码是分别用goto语句和while循环语句写的

goto语句真正合适的使用场景如下:

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

注意:多层嵌套的时候循环用goto语句是比较适合的,除此之外不建议去使用goto语句

例如:

void test()
{
flag:
	printf("hehe\n");
}
int main()
{
	goto flag;
	return 0;
}

注:goto语句只能在一个函数范围内跳转,不能跨函数。

猜数字游戏实现

猜数字游戏实现思路(步骤)
1.自动产生一个1-100之间的随机数
2.猜数字
(1)猜对了,就恭喜你,游戏结束
(2)猜错了,程序会提示你猜大了,还是猜小了,继续猜,直到猜对
3.游戏可以一直玩,除非退出游戏
原码

//猜数字游戏
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
void menu()
{
	printf("************************************\n");
	printf("********** 1. play    **************\n");
	printf("********** 0. exit    **************\n");
	printf("************************************\n");
}
void game()
{
	//猜数字游戏的实现
	//1.生成随机数
	//rand函数返回了一个0-32767之间的数字
	//时间-时间戳(时间转换出来的数字)

	int ret = rand()%100+1;  //%100的余数是0-99,然后+1,范围就是1-100
	//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));
	
	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;
}

运行结果:
在这里插入图片描述

注:

  • 采用do……while()循环是因为可以先玩,玩完之后再选择是否继续游戏
  • input变量放到do……while()循环外,这样非常巧妙的作为循环的判断条件又作为switch语句中的整形表达式。当输入0时进入case 0:入口执行当中的语句并且当跳出switch进入循环的判断条件判断出退出循环;当输入1或者其他时进入case 1:或者default:入口执行当中的语句并且当跳出switch进入循环的判断条件判断出条件成立进入循环;如果input变量定义在循环里则做不到循环判断的部分
  • rand函数的作用是生成随机数(范围是0-RAND_MAX(32767)),但在使用之前要先调用srand函数为随机生成器注入种子
  • time函数会返回一个时间戳,返回的时间戳是调用这个函数的那个时间点和计算机的起始时间之间的时间戳
  • srand函数要在rand函数之前调用,但是随机数起点的设置srand函数只需调用一次即可,整个工程中只要把随机值生成起点设置一次就可以了。这样不论如何重新生成随机值都不可能与上次的随机值一样,因为没有重新设置随机值生成起点了(如果srand设置进猜数字游戏函数里,每次玩都会调用会重新设置起点,如果生成两个随机值速度过快会导致这两个随机值是一样的)
  • srand函数的使用不需要频繁的设置生成器的起点(它可能是从起点的地方开始生成随机数)
  • srand函数如果调用多次的话生成的随机数是不够随机的
  • 如果光使用rand函数得到的随机值是固定的;如果前面使用了srand函数但种子是固定的不是变化的那么每次重新启动时得到的随机值的顺序和值都是一样的;如果前面使用了srand函数但种子是变化的不是固定的那么每次重新启动时得到的随机值的顺序和值都是不一样的(srand函数不需要频繁设置)
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值