14-结构化语言

结构化语言

C语言是结构化的程序设计语言!这里的结构化语言分为三种:

顺序结构

生活中做某个事情,从头做到尾,按顺序做

C语言中默认就是顺序结构。所以顺序结构不是重点。重点在于选择结构和循环结构

选择结构(分支语句)

有选择的做事情

C语言中把做选择的语句称之为分支语句。

注:C语言中语句是指以分号;为结尾的代码句子。称之为语句

注:表达式中非0即真(-1也是真)

注:if语句中如果不加{}则只能匹配(或者说执行)一条语句。想要执行多个语句,那需要加{}号(被{}包上的代码语句称之为代码块)

注:else语句无视层级关系,只会匹配离自己最近的if语句

注:想提高代码高质量,可以去看《高质量的C++编程》

注:return会跳出当前语句,不会执行后面的语句

语句示例
printf("MAX is : %d", max);//这是一条语句
;//这是一条空语句(空语句也能运行)

if

语法结构:
if (表达式)
	语句;
if(表达式)
	语句1;
else
	语句2;
//多分支
if(表达式1)
     语句1;
else if(表达式2)
    语句2;
else
	语句3;
return会跳出当前函数,不会继续执行下面的语句
 #include<stdio.h>
int test()
{
	if (1)
		return 0;
	printf("asdadadsadsadas\n");
	return 1;
}
int main()
{
	test();
	return 0;
}
写if判断的时候最好把常量放左边,变量名字放右边
 #include<stdio.h>
int main()
{
	int num = 3;
	//if (num = 5)//这样子写就变成了赋值
	//	printf("1\n");
	if (5==num)
		printf("2\n");
	return 0;
}

练习

1、判断一个数是否为奇数

#include<stdio.h>
int main()
{
	int num = 0;
	printf("请输入要判断数字:");
	scanf("%d", &num);
	if(num % 2 == 1)
		printf("这是奇数\n");
	return 0;
}

2、输出1-100之间的奇数

#include<stdio.h>
int main()
{
	int i = 0;
	for (i=1;i<=100;i++)//仅有C99标准支持直接在for语句里面定义in i=1
	{
		if (i % 2 == 1)
			printf("%d\n", i);

	}
	return 0;
}

switch

注:Switch括号内和case语句后面必须是常量表达式(字符/数字)

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

Switch基本语法
switch (整型表达式)
{
    case 整型常量表达式:
        语句;//每个语句项都是case语句项
}

switch的入口(case就是入口)

如果没有在case语句后面放return或者break,则case语句不会停下,会一直执行。

下图中,我们输入了1,可却输入了全部。

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

如果输入了2,则switch从case2进入

switch的出口(break就是出口)

从上文中明白了switch的入口后,再来理解switch的出口就很简单了(case是例子的意思,break是打破的意思)。也只有加入break后,才能实现真正的分支

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

一种我没见过的写法(利用switch的入口出口原理)

题:要求输入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("工作日\n");
		break;
	case 6:
	case 7:
		printf("休息日\n");
		break;
	}
	return 0;
}

默认语句default

那么,还是用前面的例子来举例,如果有猪头输入了9那该怎么办呢?

所以switch语句中得加入一个类似if语句中else类似的代码。

没错,它来了,它来了。

这就是default语句

注:default语句没有顺序之分,因为switch都是按选择来的,所以只要default语句是在switch代码块内即可

默认语句
 #include<stdio.h>
int main()
{
	int day = 0;
	scanf("%d", &day);
	switch (day)
	{
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
		printf("工作日\n");
		break;
	case 6:
	case 7:
		printf("休息日\n");
		break;
	default:
		printf("这是默认语句\n");
		break;
	}
	return 0;
}

题目练习

正确答案是5、3

 

循环结构(循环语句)

循环的做某个事情

C语言中把做循环的语句称之为循环语句

While

注:在while循环中,break用于永久的终止循环,continiu的作用是跳过下面的代码,直接回到判断去了(continiu的作用是跳过本次continiu后面的代码,直接去判断部分,看是否进行下一次循环 )

表达式如果为非0(为真),会被执行。下列代码中循环 语句可以是代码块。

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

循环打印1-10

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

代码分析

下列代码中,

stdio.h是标准输入头文件

getchar返回类型是int类型

getchar是用于获取字符的。

如果在读取过程中遇到错误(或者遇到EOF文件结束)。如果正确读取,返回的是键入的ASCII码值。读取错误就是EOF。EOF本质上就是个-1,而-1不属于ASCII码值,所以会文件结束

putchar是输出字符的。

putchar(ch)就是输出ch函数的字符

注:如果在程序运行过程中按下ctrl+C,实际上getchar读取到的就是EOF。所以会退出程序

getchar读取单个字符。它并不是直接读取键盘,它是在等待缓冲区,当我们在键盘按下A时并且敲下回车,这里键盘实际上就把A\n放入了缓冲区内此时getchar会读取A,返回A的ascii码,putchar会根据返回的ASCII码还原对应的字符A,并且在屏幕上输出,然后getchar会再次读取\n,并且返回打印,所以此时屏幕上便出现了自动打印A并且回车的效果(getchar会认为缓冲区里所有东西都是字符。)

scanf与getchar这些函数都是从缓冲区中获取数据(如果没有数据则等待)

下图中,getchar直接读取了缓冲区剩下的\n。

那么如何解决这个问题呢?

我们可以在scanf拿走缓冲区的东西后,清空缓冲区的数据,让getchar无法获取数据,让它只能乖乖等待。

所以我们要学会清理缓冲区。

getchar();//处理\n(读了就读了)

===========================================

scanf只会读取空格前的字符(所以输入密码时会要求不要键入空格,C编译在碰到空格,TAB,回车或非法数据(如对“%d”输入“12A”时,A即为非法数据)时即认为该数据结束。)

getchar();只能消耗一个字符。想要解决问题,还可以用到while循环清理缓冲区的空语句

============================================

下图的代码中,只会打印数字字符,非数字的都不会打印

=============================================

可以看出,while的循环中,变量的初始化和判断部分以及调整部分都十分分散。

#include<stdio.h>
int main()
{
	int i = 1;//初始化
	while (i <= 10)//判断部分
	{
		printf("%d",i);
		i++;//调整部分
	}
	return 0;
}

for

for循环中,表达式1为初始化,表达式2为判断,表达式3为调整

C语言中,循环先用for,其次是while,最后是while

for (表达式1; 表达式2; 表达式3)
{
	循环语句;
}

上面while的循环用for可以这么写

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

for循环的执行流程

第一次进入for循环后,先执行表达式1,也就是初始化部分,然后进入判断部分(不符合就不循环,符合就循环),当判断部分执行完后就进入到调整部分。调整部分执行完后再执行判断部分

===============================================

因为循环中执行到5时被break出去。所以最后打印出来的是1234

====================================================

continiu的作用是跳出本次循环后面的代码,当i=5时,continiu就直接跳回调整部分i++,此时i就等于6.所以最后打印出来的是1234678910

注:Break的作用是终止循环,continiue的作用是直接不循环本次了,直接重开

 for语句的循环控制变量建议

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

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

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	for (i = 0; i < 10; i++)//i=0是左闭,i<10是右开,i<=10是右闭
	{
		printf("%d",arr[i]);
	}
	//建议使用i<10这种写法,这种写法一看就是循环10次
	return 0;
}

一些for循环的变种

#include<stdio.h>
int main()
{	
	//for循环中,初始化,判断、调整这三个部分都可以省略
	//判断部分的省略使得恒为真,导致死循环,所以大家要慎重省略
	for (;;)
	{
		printf("haha\n");
	}
	return 0;
}
#include<stdio.h>
int main()
{	
	int i, j;
	//i循环一次,j要循环三次,
	//所以最后输出九次
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3; j++)
		{
			printf("hehe\n");
		}
	}
	return 0;
}
#include<stdio.h>
int main()
{	
	int i = 0;
	int j = 0;
	//i的循环只会执行1次,执行1次i的循环则j要执行3次,当j=4时,就跳出循环,此时i=2,还是小于3,进入i循环,但是j已经=4了,所以不能执行j的循环了(都是没有初始化的锅)
	for (; i < 3; i++)
	{
		for (; j < 3; j++)
		{
			printf("hehe\n");
		}
	}
	return 0;
}

下图中循环为0次,因为for循环中判断部分为赋值,k=0,0为假,所以最后这个循环一次都没循环。所以循环0次

do while

do语句的特点:循环体至少被执行一次,使用的场景有限,所以不是经常使用

do while的执行流程是,不管怎么样,先上来执行do,执行完后再判断,同样,这里的循环语句可以是一句代码也可以是代码块

 

下列这个代码就是当i=5时,退出循环,所以它只会打印出1234

练习:

 

n的阶乘就是指计算从1*2*3*4*5......*n=n的阶乘(0的阶乘就是1,阶乘都是从0开始的)

1!代表的就是1的阶乘

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

注:上面的代码只是单纯的求出某个数的阶乘值

计算1!+2!+3!+4!+5!+6!+7!+8!+9!+10!
#include<stdio.h>
int main()
{	
	int i = 0;
	int n = 0;
	int ret = 1;
	int sum = 0;
	for(int n=1;n<=10;n++)
	{ 
		ret = 1;
		for (i = 1; i <= n; i++)
		{
			ret *= i;
		}
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;
}

上面这个代码比较繁琐一点,其实可以更简化一点,例如,我们需要求出5!的阶乘,而5的阶乘是由1*2*3*4*5而来的。4!的阶乘是由1+2+3+5而来的,所以计算5的阶乘可以简化为5*4!。也就是说,我们要想求阶乘n,只需要知道n-1的阶乘。最后再n*(n-1)即可

简化版求3!的阶乘之和
 #include <stdio.h>
int main()
{
	int ret = 1;
	int sum = 0;
	for (int i = 1; i <= 3; i++)
	{
		ret *= i;
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;
}

 

折半查找法/二分法

它的效率是非常高的,例如我们要查64个数字中的其中一个,只需要查以2为底的log64(2的六次方)次方(也就是说只要查6次)。算法的思想就是找出这组数据中中间元素的下标,然后拿这个下标和要查找的数字进行对比

举个例子:某个int a[10]={1,2,3,4,5,6,7,8,9,10};现在要求我们去查找数字7。

我们使用二分法进行查找,第一次寻找。

最左边的下标为0,最右边的下标为9

(0+9)/2=4

4(实际就是元素5)<7

4+1

第二次寻找开始,

最左边下标为5,最右边下标为9

(5+9)/2=7

7(实际上就是元素8)>7

7-1

第三次寻找开始,

最左边下标为5,最右边下标为6

(5+6)/2=5

5(实际上元素是6)<7

5+1

第四次寻找开始

最左边下标是6,最右边下标也是6.

(6+6)/2=6

所以最后排除6,就剩下元素7.所以7就这么被找到了。这个范围,做下标是6,右下标也是6,平均值也是6,6对应的元素值又是7.就这么被找到了

用代码表示二分法

用代码表示二分法
 #include <stdio.h>
int main()

{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int k = 9;//要被查找的数字
	//就是说要在arr这个数组中查找k(7)的值
	int size = sizeof(arr) / sizeof(arr[0]);
	int left = 0;
	int right = size - 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,值为%d\n", mid,arr[mid]);
			break;
		}
	}

	return 0;
}

第四题的题目意思是酱紫的

查看代码
 #include <stdio.h>
#include <string.h>
#include <windows.h>
int main()

{
	//我们需要准备两个数组用来存放变身前和变身后,
	char arr1[] = "welcome to bit!!!!!!";
	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);//增加动态效果,延迟1000毫秒执行下一个代码
		system("cls");//清空屏幕,实现打印出来的字符会有动画效果
		left++;
		right--;
	}
	printf("%s\n", arr2);//保证程序运行到最后,还能打印出字符(没这句了话,则屏幕最后也会被cls掉)
	return 0;
}
输入登录密码错误三次
 #include <stdio.h>
#include <string.h>//字符串头文件
#include <windows.h>
int main()

{
	//编写代码实现,模拟用户登录情景,并且只能登录三次。( 只允许输入三次密码,如果密码正确则提示登录成,如果三次均输入错误,则退出程序。
	//假设正确密码是123456
	int i = 0;
	char password[20] = { 0 };
	for (i = 0; i < 3; i++)
	{
		printf("请输入密码(第%d次机会,总共3次):",i+1);
		scanf("%s", password);
		//if(password=="password")//err这种写法是错误的,不能使用==来进行比较,应该使用strcmp来比较
		if (strcmp(password, "123456") == 0)//r如果输入的值等于正确密码123456,strcmp用于比较两个字符串并根据比较结果返回整数。基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数
			{
				printf("恭喜您,登陆成功\n");
				break;
			}
	}

	if (i == 3)
	printf("登录失败\n");
	return 0;
}
	

一个猜数字游戏,会自动产生一个1-100之间的随机数,猜对了就恭喜你,猜错了就告诉你是猜大了还是猜小了。游戏可以一直玩,除非退出游戏。

随机生成随机数
 #define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void menu()
{
	printf("******************************\n");
	printf("***********1.开始游戏*********\n");
	printf("***********0.离开游戏*********\n");
	printf("******************************\n");
}
void game()
{
	//猜数字的游戏实现
	//1、先生成随机数,生成随机数有一个库函数,名字叫rand,使用的的库名字叫stdlib.h。rand函数无参,返回的数据类型为整型,rand的取值在0到RAND_MAX之间,RAND_MAX的值为0x7fff。转为十进制为0-32767之间的数值,在调用rand函数产生随机值之前,应该先调用srand函数,srand函数的组用是设置一个随机的起点。随着srand值的不同,rand产生的随机值也不同。也就是说只要srand产生的是随机值,那么rand产生的也是随机值。电脑的时间一直在发生变化,所以我们可以把时间戳传递进去(时间戳就是时间转换成的数值),那怎么获取时间戳呢?有个函数叫time函数,time()就会返回一个时间戳 ,但是time函数返回的是_int64类型,而srand函数又需要的是unsigned int类型的值,所以我们需要把time的返回值强制类型转换unsigned_int类型。tim函数需要的库文件是time.h
	srand((unsigned int) time(NULL));
	int ret = rand();
	printf("随机值为:%d\n",ret);
}
int main()
{
	//根据游戏要求,希望游戏结束还能再玩一把,所以我们可以采用的循环结构是do while循环,这样子不管怎么样,都会先执行一次游戏
	int input = 0;//变量input为用户的选择
	do
	{
		//游戏开始我们应该先答应一个菜单,打印菜单太麻烦了,还是做成一个函数吧
		menu();
		printf("请选择:>");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			game();
			printf("猜出字符\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
			
	} while (input);
	return 0;
}

注:时间戳的产生原理是计算出当下时间和电脑开始时间的差值

但是上面这个代码还是有问题的,就是每次输入1,都会产生相近的随机值,这样子下去完全有可能会遇到一起,这是什么原因导致的呢?

是下面这个代码导致的,因为时间戳是按时间来的,把下面这个代码放在game函数中,就变成了玩一次产生一次值,如此是不合适的。要想解决问题,应该只让起点只产生一次,所以我们只需要把下面的代码放进main函数内开头处即可。

srand((unsigned int) time(NULL));

 

完整代码
 #define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void menu()
{
	printf("******************************\n");
	printf("***********1.开始游戏*********\n");
	printf("***********0.离开游戏*********\n");
	printf("******************************\n");
}
void game()
{
	//猜数字的游戏实现
	//1、先生成随机数,生成随机数有一个库函数,名字叫rand,使用的的库名字叫stdlib.h。rand函数无参,返回的数据类型为整型,rand的取值在0到RAND_MAX之间,RAND_MAX的值为0x7fff。转为十进制为0-32767之间的数值,在调用rand函数产生随机值之前,应该先调用srand函数,srand函数的组用是设置一个随机的起点。随着srand值的不同,rand产生的随机值也不同。也就是说只要srand产生的是随机值,那么rand产生的也是随机值。电脑的时间一直在发生变化,所以我们可以把时间戳传递进去(时间戳就是时间转换成的数值),那怎么获取时间戳呢?有个函数叫time函数,time()就会返回一个时间戳 ,但是time函数返回的是_int64类型,而srand函数又需要的是unsigned int类型的值,所以我们需要把time的返回值强制类型转换unsigned_int类型。tim函数需要的库文件是time.h
	//产生1-100的随机数。rand%100的余数是0-99,然后+1,范围就是1-100
	int ret = rand()%100+1;
	printf("随机值为:%d\n",ret);
	int guess = 0;
	while (1)
	{
		printf("请猜数值:>");
		scanf("%d",&guess);
		if(guess<ret)
			printf("猜小了\n");
		else if (guess > ret)
		{
			printf("猜大了\n");
		}
		else
		{
			printf("猜对了\n");
		}
	}
}
int main()
{
	//根据游戏要求,希望游戏结束还能再玩一把,所以我们可以采用的循环结构是do while循环,这样子不管怎么样,都会先执行一次游戏
	srand((unsigned int)time(NULL));
	int input = 0;//变量input为用户的选择
	do
	{
		//游戏开始我们应该先答应一个菜单,打印菜单太麻烦了,还是做成一个函数吧
		menu();
		printf("请选择:>");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			game();
			printf("猜出字符\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
			
	} while (input);
	return 0;
}

 

posted @ 2022-09-09 09:52  裆朝大学士  阅读(463)  评论(0)    收藏  举报