目录
5.(小插曲)getchar和putchar和标准输入缓冲区
3. 在一个有序数组中查找具体的某个数字n。(讲解二分查找)
5. 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则 提示登录成,如果三次均输入错误,则退出程序。
1.&&的注意事项
int main()
{
int age=0;
scanf("%d",&age);
if(age<18)
{
printf("未成年\n");
}
else if(18<=age<30)
{
printf("青年\n");
}
return 0;
}
这样无论怎么输入都会打印青年
因为(18<=age<30)是先判断前面,18<=50为真1,再1<=30也为真。需要改为(18<=age && age<30)。
2.悬空else
这段代码的执行结果什么都不会输出。
else和离得近的语句进行匹配
改进:
int main()
{
int a = 0;
int b = 2;
if( a == 1 )
{
if(b == 2)
{
printf("hehe\n");
}
}
else {
printf("haha\n");
}
return 0;
}
3.switch语句
case决定入口
break决定出口
switch(整型表达式)
{
语句项;
}
而语句项是什么呢?
//是一些case语句:
//如下:
case 整形常量表达式:
语句;
如果像下面这样写的话输入5就会把后面的打印出来,执行流程是这样的:
改进:
int main()
{
int day = 0;
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("输入错误\n");
break;
}
return 0;
}
有时候我们的需求变了:
1. 输入1-5,输出的是“weekday”;
2. 输入6-7,输出“weekend”
//switch代码演示
int main()
{
int day = 0;
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");
}
return 0;
}
打印的结果为m=5,n=3 。自己的break只能跳出自己的语句
4.循环语句
break:
其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。 所以:while中的break是用于永久终止循环的。
打印的结果是1 2 3 4
Continue:
continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行, 而是直接跳转到while语句的判断部分。进行下一次循环的入口判断。
这段代码的结果是1 2 3 4 死循环
因为i == 5的时候continue会跳过下面的代码,然后i还是5
5.(小插曲)getchar和putchar和标准输入缓冲区
getchar - 读取一个字符
putchar - 输出/打印一个字符
getchar 读取到一个字符的时候返回的是一个字符的ASII码值
如果读取失败,返回的是EOF,而EOF是一个整型的-1。所以是int ch = getchar()
清空标准输入缓冲区的重要性
但是这样又会出错
getchar只会处理\n
改进
如果scanf要读取123 abc的话可以这样写(意义不大)
意思为一直向后读,直到遇到\n
6.for循环
语法:
for(表达式1; 表达式2; 表达式3)
循环语句;
表达式1
表达式1为初始化部分,用于初始化循环变量的。(只会执行一次)
表达式2
表达式2为条件判断部分,用于判断循环时候终止。
表达式3
表达式3为调整部分,用于循环条件的调整。
for语句的循环控制变量
建议:
1. 不可在for 循环体内修改循环变量,防止 for 循环失去控制。
2. 建议for语句的循环控制变量的取值采用“前闭后开区间”写法。
如这样就是错的:
前闭后开
for循环的初始化、判断、调整三个部分均可以省略
如果把判断部分省略了,就意味着判断恒为真
例题:
条件判断中是赋值,0为假所以不会进去。
7.do...while()循环
do语句的语法:
break在其中的结果:
continue在其中的结果:
结果为死循环
总结:
8.例题
1.计算n的阶乘:
#include <stdio.h>
int main()
{
int total,n;
scanf("%d",&n);
for (total=1;n>0;n--)
{
total*=n;
}
printf("%d",total);
}
有这样一个宏定义:
所以不能计算100的阶乘,会溢出。
2. 计算 1!+2!+3!+……+10!
int main()
{
int total=1,i,sum=0,n;
for (n=1;n<=10;n++)
{
for (i = 1,total=1; i <= n; i++)
{
total *= i;
}
sum += total;
}
printf("%d\n",total);
printf("%d\n",sum);
}
代码优化:
思路算1的时候是1*1
算2的时候是 1*2
算3的时候是 1*2*3
算4的时候是 1*2*3*4
所以每次在算的时候就上一个阶乘乘以自身就可以了
#include <stdio.h>
int main()
{
int total=1,sum=0,n;
for (n=1;n<=10;n++)
{
total *= n;
sum += total;
}
printf("%d\n",total);
printf("%d\n",sum);
}
3. 在一个有序数组中查找具体的某个数字n。(讲解二分查找)
⚠️注意二分查找的前提是有序的
这里首先看找一个数,普通的方式就是遍历进行查找
#include <stdio.h>
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9,10};
// 0 1 2 3 4 5 6 7 8 9
int k = 7,i=0;
int sz=sizeof (arr)/ sizeof(arr[0]);
int flag;
for (i=0;i<sz;i++)
{
if(k==arr[i])
{
flag =1;
printf("find element,arr:%d\n",i);
break;
}
}
if(flag == 0)
{
printf("not find\n");
return 0;
}
}
但是这样进行查找的话,就算一个无序的数组这样查找的话也可以。而现在是有序的这样反而显得效率并不高。
参考旧金山大学的动画演示
Binary and Linear Search Visualizationhttps://www.cs.usfca.edu/~galles/visualization/Search.html
#include <stdio.h>
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9,10};
// 0 1 2 3 4 5 6 7 8 9
int k = 7;
int sz=sizeof (arr)/ sizeof(arr[0]);
int flag=0;
int mid,left,right;
left=0;
right=sz-1;
while (left<=right)
{
mid=(left+right)/2;//mid只能放在循环里面每次需要重新定位mid
//但是这样如果数特别大很容易溢出,所以可以改进为
//mid=left +(right-left)/2
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
printf("find element arr%d", mid);
flag = 1;
break;
}
}
if (0==flag)
{
printf("not find\n");
}
return 0;
}
溢出改进的图示:
4. 编写代码,演示多个字符从两端移动,向中间汇聚。
#include <stdio.h>
#include <string>
#include <unistd.h>
#include <stdlib.h>
int main()
{
char arr1[]="hello world";
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);
usleep(1000000);
system("clear");
left++;
right--;
}
printf("%s\n",arr2);
return 0;
}
这是在Mac的Clion中所使用的。usleep意思是延迟输出,system意思为清空输出框的显示
5. 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则 提示登录成,如果三次均输入错误,则退出程序。
假设密码为123456
strcmp函数的基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数12。
注意这里不能写成if("123456"==password),字符无法这样比较。
#include <stdio.h>
#include <string>
int main()
{
int i;
char password[20]={0};
int flag=0;
for (i=0;i<3;i++)
{
printf("请输入密码\n");
scanf("%s",password);
if(strcmp("123456",password)==0)
{
flag=1;
printf("密码正确\n");
break;
}
else
{
printf("密码错误\n");
}
}
if(flag==0)
{
printf("输入三次错误,密码退出");
}
return 0;
}
在这篇代码中如果 char password[20]={0};写成了 char password[]={0};那么表示这个数组只有一个字符的空间,然后试图在这个数组中存储长度为6的字符串"123456"。这会导致越界,因为这个数组只能存储一个字符。
正确的做法应该是使用一个足够大的字符数组来存储密码。如:
char password[]={1,2,3,4,5,6};或char password[20]={0};
⚠️strlen只针对字符串,只能计算字符串长度。sizeof计算变量,空间,数组所占空间大小的,不关心内容。strlen是库函数,sizeof是操作符。
9.猜数字游戏实现
9.1插曲:随机数的生成
有一个库函数rand()
Time函数会返回一个指针,不需要所以写成 time(NULL)
#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()
{
int ret;
//1.生成随机数
ret =rand()%100+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;
}
10.goto语句
#include <stdio.h>
int main()
{
begain:
printf("hehe\n");
goto begain;
return 0;
}
C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。
从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。 但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过 程。 例如:一次跳出两层或多层循环。 多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。
goto语言真正适合的场景如下:
for(...)
for(...)
{ for(...)
{ if(disaster) goto error; }
} …
error:
if(disaster) // 处理错误情况
如果想从三个for循环里出来就要三个break,而go to就很方便了。
小游戏:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
//使用命令行关机
//shutdown
//-s设置关机 -t设置时间关机
//shutdown -s -t 60设置60s后关机
//shutdown -a 取消关机
char arr[20]={0};
system("shutdown -s -t 60");
agagin:
printf("请注意,你的电脑在1分钟之内关机,如果输入:我是猪,就取消关机\n");
scanf("%s",arr);
if(strcmp(arr,"我是猪")==0)
{
system("shutdown -a");
} else{
goto agagin;
}
return 0;
}