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


浙公网安备 33010602011771号