上一章节我完成了绘制草图的任务,从本章开始我将进入到打地基的阶段,本节我会详细分享C语言中分支语句和循环语句的知识点,同时配有大量的练习,在练习过程中,涉及到很多陌生的知识,但如果已经仔细掌握了上一篇的内容,还是完全可以理解的,我们不能一直呆在舒适区,适当的难度增加能让我们更快的进步,话不多说,let's go!!!
分支语句和循环语句
语句:
C语言中的把语句分为以下五种类型:表达式语句、函数调用语句、控制语句、复合语句、空语句
本章节主要介绍控制语句:
C语言是结构化的程序设计语言,在C语言程序设计的过程当中,目的是要把生活中的问题用计算机的方式解决,生活中万事万物均可拆分为以下三种结构或三种结构中几种的组合:顺序结构、选择结构、循环结构,C语言能表述这种结构化,而这种表述主要依靠C语言中的控制语句来实现,控制语句:用于控制程序实行的流程,以实现程序的各种结构方式,它们由特殊的语句定义符组成,C语言中共有9种控制语句,分为以下三类:
1.条件判断语句也叫分支语句:if语句、switch语句;
2.循环执行语句:do while语句、while语句、for语句;
3.转向语句:break语句、goto语句、continue语句、return语句;
分支语句(选择结构)
生活中同一事件根据情况不同,有不同的选择,C语言中实现这种选择的就是分支语句
if语句:通常用于三分支及以下
语法结构:
-
if(表达式)//单分支,表达式为真,语句就执行
语句;
-
if(表达式1)//双分支,哪个表达式为真,选择哪部分语句执行
{
语句1;
}
else(表达式2)
{
语句2;
}
-
if(表达式1)//多分支,只会选择一个部分执行,哪部分表达式为真,选择哪部分语句执行
{
语句1;
}
else if(表达式2)
{
语句2;
}
……
else(表达式n)
{
语句n;
}
//1.#include<stdio.h>
int main()
{
if (3 == 10)
printf("hehe\n");//表达式是假的,不打印
return 0;
}
//2.#include<stdio.h>
int main()
{
int a = 3;
if (a == 3)//表达式为真,打印出hehe
printf("hehe\n");//表达式是假的,不打印
return 0;
}
if语句代码风格注意点:
*C语言中如何表示真假?0表示假,非0表示真。
**书写分支语句时的规范性:
1.if和else后面的语句块可以执行不止一条语句,注意要用大括号把每一部分整体括起来,以免出现因else与最近的if适配这一规则等问题而导致程序出现bug
#include<stdio.h>
int main()
{
int a = 1;//运行发现这段代码不打印任何东西
int b = 2;//这里就涉及到else的匹配问题
if (a == 2)//之所以没有打印结果,是因为else是和离自己最近的if匹配的
if (b == 2)//观察到如右所示的代码在第一个if判断表达式真假的时候
printf("hehe");// 结果就为假,因此就不再执行这一部分语句
else // 自然是什么也不打印
printf("haha");
return 0;
}
2.当在写表达式的时候,为避免把表示相等的关系操作符“==”错写成表示赋值的操作符“=”后系统难以发现问题,我们一般写成“常数==a”,这样一旦错写成“常数=a”系统就会进行报错,因为不可能把一个变量赋值给常数,而“a=常数”本身没有问题,则往往不会被报错(a通常是一个已经赋值的变量),而当一个“表达式==常数”时则无须写成“常数==表达式”的形式,这是因为常数只能赋值给变量,当它赋值给表达式的时候,系统是能检测并且报错的,即“表达式=常数”本身就是错的。
int main()
{
int a = 1;
int b = 2;
if (a == 2)
{
if (b == 2)//好的格式可写成(2==b)
printf("hehe");
}
else
{
printf("haha");//此时就可以打印出结果为haha
}
return 0;
}
要想写出高质量的程序,则必须注意代码书写的规范性,如上列提及的问题,类似的规范问题还有:变量的命名最好要有意义,且其形式利用大小写、空格、下划线等方式让读者更方便明白其含义,整个程序书写过程中也要合理利用空格和换行,使其更加具有可读性
这里推荐代码形式规范的一本好书:高质量的C/C++编程:林锐博士
- 练习一:输入一个整数判断是否为奇数
#include<stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
if (num % 2 == 1)//判断一个数是否为奇数,表达式本身就不能赋值,这个不需要调换表达式num % 2 和1的位置
printf("奇数\n");
else
printf("不是奇数");
return 0;
}
- 练习二:打印1-100范围内的奇数
Switch:通常用于多分支
语法结构:
switch(整型表达式)//必须是整型表达式!!
{
语句项;
}
语句项是什么?是一些case语句,如下:
case 整形常量表达式:
语句;
//方法一: #include<stdio.h> int main() { int i = 0; for (i = 0; i <= 100; i++)//利用for循环 { if (i % 2 == 1) printf("%d\n", i); } return 0; } //方法二: #include<stdio.h> int main() { int i = 0; while (i <= 100)//利用while循环 { if (i % 2 == 1) printf("%d\n", i); i++; } return 0; } //方法三: #include<stdio.h> int main() { int i = 1; while (i <= 100) { printf("%d\n", i); i = i + 2;//不用i++也能实现 } return 0; }
switch语句代码风格注意点:
*整形常量表达式中,字符可以放,因为字符的存储的本质是存储其ASCII值,ASCII码值属于整型常量
**要想真正实现分支就要搭配break
1.一个语句项搭配一个break,实现单独的选择
2.多个语句项搭配同一个break,实现分类选择
3.break语句的实际效果是把语句列表划分为不同的分支结构,编程好习惯,最后一个语句最好加break
*** switch 语句搭配default子句更严谨
如果表达的值与所有的case标签的值都不匹配怎么办? 其实也没什么,结构就是所有的语句都被跳过而已。 程序并不会终止,也不会报错,因为这种情况在C中并不认为是个错误。 但是,如果你并不想忽略不匹配所有标签的表达式的值时该怎么办呢? 你可以在语句列表中增加一条default子句,当 switch 表达式的值并不匹配所有 case 标签的值时,这个 default 子句后面的语句就会执行。default可以写在任何一个 case 标签可以出现的位置,而且语句流会像执行一个case标签一样执行default子句。每个switch语句中只能出现一条default子句,编程好习惯:在每个 switch 语句中都放一条default子句是个好习惯,甚至可以在后边再加一个 break 。
****Switch语句可以嵌套使用,每个break只能跳出自己所在的语句块
//1.
#include<stdio.h>
int main()
{
int date = 0;
scanf("%d", &date);
switch (date)
{
case 1:
printf("星期一\n");//发现如果输入4,则会打印星期四-星期日
case 2:
printf("星期二\n");//是因为case本身只能决定开始的入口,而若想要执行完某句就结束,即要实现真正的分支
case 3:
printf("星期三\n");//就应该再找一个出口,这时就要用到break语句,具体见下段代码
case 4:
printf("星期四\n");
case 5:
printf("星期五\n");
case 6:
printf("星期六\n");
case 7:
printf("星期日\n");
}
return 0;
}
//2.
#include<stdio.h>
int main()
{
int date = 0;
scanf("%d", &date);
switch (date)
{
case 1:
printf("星期一\n");//这个时候,输入数字几,就能打印星期几
break;
case 2:
printf("星期二\n");//因为一旦找到了相应的执行入口,执行完后,有break就结束继续向下运行,从而实现了分支
break;
case 3:
printf("星期三\n");//变个需求,现在输入1-5 输出weekday,输入6-7 输出weekend
break;
case 4:
printf("星期四\n");//这个时候就需要多个语句搭配一个break,见下一个代码段
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期日\n");
break;
}
return 0;
}
//3.
#include<stdio.h>
int main()
{
int date = 0;
scanf("%d", &date);
switch (date)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("weekday\n");//现在输入1-5 输出weekday,输入6-7 输出weekend
break;
case 6:
case 7:
printf("weekend\n");//只要没遇到break,就继续向下执行,遇到break就立刻停止执行
break;//又出现新的问题,如果输入的值不在语句项包含的范围之内呢?如输入8,详见下一个代码块
}
return 0;
}
//4.
#include<stdio.h>
int main()
{
int date = 0;
scanf("%d", &date);
switch (date)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("weekday\n");
break;
case 6:
case 7:
printf("weekend\n");
break;
default:
printf("选择错误!");
break;
}
return 0;
}
小练习:试计算下列代码运行结果
#include<stdio.h>
int main()
{
int n = 2;
int m = 4;
switch (n)
{
case 2:
n++;//n=3
case 3:
m++;//m=5
switch (n)
{
case 2:
m++;
case 3:
m++, n++;//m=6,n=4
break;
}
case 4:
m++;//m=7
break;//到此结束
default:
break;
}
printf("%d %d", m, n);//打印结果为7 4
return 0;
}
循环语句
while语句
我们已经知道了if语句的:当条件满足的情况下,if语句后的语句执行,否则不执行,但是这个语句只会执行一次。那同一件事情要完成很多次的时候,我们应该怎么做呢?C语言中引入了while语句,可以实现循环
语法结构:
while(表达式)//表达式为真,进入循环,直到表达式为假就跳过
{
遇到break,整个循环就终止,用于永久的终止循环
遇到continue ,会跳过本次循环,判断下一次循环是不是执行,能执行就继续执行
}
//1.
#include<stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
printf("%d\n", i);//在屏幕上打印1-10
i++;
}
return 0;
}
//2.
#include<stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
if (5 == i)
break;//此时打印出来的是2345,当i=5时,break语句执行,跳出循环
i++;
printf("%d", i);
}
return 0;
}
//3.
#include<stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
i++;
if (5 == i)
break;//此时打印出来的是234,当i=5时,break语句执行,永久跳出循环
printf("%d", i);
}
return 0;
}
//4.
#include<stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
i++;
if (5 == i)
continue;//此时打印出来的是2 3 4 6 7 8 9 10 11,当i=5时,continue语句执行,跳过本次循环,直接进行下次循环
printf("%d ", i);
}
return 0;
}
看一段重要的代码
#include int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
} //这里的代码适当的修改是可以用来清理缓冲区的.
*为什么getchar()赋值的变量用整型来定义而不是用字符char来定义?
getchar():从键盘上获取一个字符 ,如果读取正常,返回的是字符本身,如果读取错误或者异常,返回的是EOF(文件结束标志:end of file),而EOF的本质是#define EOF(-1),(-1)是一个整数,这个时候getchar()就最好用整型来接受,如果用char,其大小是一个字节,而EOF是整数(-1),需要四个字节才放的下,因此直接用int来定义,即可以放下正常字符的ASCII码值,又可以放下EOF即-1.
int ch=getchar(),代表将获取的字符的ASCII码值放到变量ch里面,**printf("%c\n",ch);**可以把获取的ASCII码值用字符的形式打印出来,那么除了这种打印方式还有一种打印方式是**putchar(ch);**这两种写法是一样的
getchar()停止执行的快捷键是Ctrl+z,Ctrl+z的本质是让getchar()返回的值是EOF.
我们接下来看它在几段代码中的应用:
//1.举个例子,输入密码并确认是否正确
#include<stdio.h>
int main()
{
char password[20] = { 0 };
printf("请输入密码:>");//假设密码是个字符串
scanf("%s", password);//和getchar()一样,从缓冲区里面拿数据
printf("请确认密码(Y/N):");//它只拿走了缓冲区里面的密码,还剩下一个\n
int ret = getchar();//直接从缓冲区里拿走了\n
if ('Y' == ret)//\n!=Y,所以直接到了else语句,执行打印
{
printf("Yes\n");
}
else
{
printf("No\n");//没有让我自己输入,直接输出了No
}
return 0;
}//为了实现这个程序原本计划的功能,就要想办法把缓冲区的\n给去掉
//2.想办法把缓冲区的\n给去掉
#include<stdio.h>
int main()
{
char password[20] = { 0 };
printf("请输入密码:>");
scanf("%s", password);
getchar();//把\n读取了
printf("请确认密码(Y/N):");
int ret = getchar();//这里的getchar就又等待了
if ('Y' == ret)
{
printf("Yes\n");
}
else
{
printf("No\n");//此时可以正常实现功能
}//但这种办法也不是完全没有bug的,当输入的密码中间有空格,空格前scanf读取,下面第一个getchar()只读取了一个空格
return 0;//继续执行,第二个getchar()拿到的只是空格后的第一个字符,此时条件又不成立,直接执行else语句
}//这个时候前面看的那段用于清理缓冲区的代码就派上了用处
//3.
#include<stdio.h>
int main()
{
char password[20] = { 0 };
printf("请输入密码:>");
scanf("%s", password);//并不是说密码里面不可以有空格,而是scanf读取的特点就是遇到空格就停止
int ch = 0;
while ((ch = getchar()) != '\n')//每次读取一个字符
{
;//直到找到\n就停止
}
printf("请确认密码(Y/N):");
int ret = getchar();
if ('Y' == ret)
{
printf("Yes\n");
}
else
{
printf("No\n");
}
return 0;
}
练习:输入任意字符但只能打印其中的数字字符
#include <stdio.h>
int main()
{
char ch = '\0';
while ((ch = getchar()) != EOF)
{
if (ch < '0' || ch > '9')
continue;
putchar(ch);
}//只打印数字字符
return 0;
}
for语句
while循环初始化、判断、调整,结构有时候较长,因此易错,此时
for循环出现
语法结构:
for(表达式1; 表达式2; 表达式3)
{
循环语句;
遇到break,整个循环就终止,用于永久的终止循环
遇到continue ,会跳出本次循环,直接跳到表达式3
}
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//十个数据的下标分别是0~9
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
表达式1为初始化部分,是用于初始化循环变量的,整个过程中执行一次
表达式2为条件判断部分,用于判断循环什么时候终止。
表达式3为调整部分,用于循环条件的调整。
可以发现在while循环中依然存在循环的三个必须条件,但是由于风格的问题使得三个部分很可能偏离较 远,这样 查找修改就不够集中和方便。所以,for循环的风格更胜一筹;for循环使用的频率也最高。
- 使用while语句时,很容易出现以下错误,同时改为for 就会大大减小出错概率:
//1.使用while
#include <stdio.h>
int main()
{
int i = 1;//初始化
while (i<=10)//判断
{
if (i == 5)
continue;
printf("%d ", i);
i++;//调整,这个时候i++在循环体里面,当i==5,continue后面的语句不执行,直接又到了判断部分,但此时i仍然为5,就进入了死循环
}//最终只能打印1 2 3 4,然后程序进入死循环(光标在4后面持续闪烁)
return 0;
}
//2.使用for
#include <stdio.h>
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)//实际上i++是不在循环体里面的
{
if (i == 5)
continue;//不执行continue后面的代码直接跳到调整部分,即表达式3:i++;(可以把for循环的顺序看成一个圈)
printf("%d ", i);//此时可以打印1 2 3 4 6 7 8 9 10
}
return 0;
}
for语句代码风格注意点:
*尽量不要在循环体里随便修改循环变量
**判断部分如果省略,意味着判断恒成立
***不要随便省略初始化,否则嵌套使用for循环中很容易出错
//1.初始化部分如果完整
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)//初始化部分如果完整
{
for (j = 0; j < 3; j++)
{
printf("hehe\n");//共会打印9次hehe
}
}
return 0;
}
//2.初始化部分省略
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)//初始化部分如果省略
{
for (; j < 3; j++)
{
printf("hehe\n");//共会打印3次hehe,因为在外面的for循环进行一次,里面的for循环进行3次,此时j的值为3
}//外面的for 循环进行第二次循环时,j未初始化,j=3无法进入里面的for循环,直接跳出
}
return 0;//因此不要在写for循环时,省略初始化部分
}
****不要在for循环的表达式1,初始化部分创建变量,不是所有编译器都能识别的,(C99语法中才可以,C++语法中可以)
*****判断部分注意相等==和赋值=的问题,如果是赋值为0,则会判断条件为假,不进入循环
do while 语句
语法结构:
do
{
循环语句;
遇到break,整个循环就终止,用于永久的终止循环
遇到continue ,会跳出本次循环,即跳出大括号,直接跳到判断部分去,进行下一次
}
while(表达式);
不难看出,do while语句至少会循环一次,这种循环语句的使用场景有限,用的很少
//1.
#include <stdio.h>
int main()
{
int i = 10;
do
{
printf("%d\n", i);//只执行一次
}
while(i<10);
return 0;
}
//2.
#include <stdio.h>
int main()
{
int i = 10;
do
{
i++;
printf("%d\n", i);//打印结果为 11 12 13 14 15
}
while (i < 15);
return 0;
}
goto语句
C语言中提供了可以随意滥用的 goto语句和标记跳转的标号。从理论上 goto语句是没有必要的,实践中没有goto语句也可以很容易的写出代码。但是某些场合下goto语句还是用得着的,最常见的用法就是终止程序在某些深度嵌套的结构的处理过程。另外,goto语句不可跨函数跳转。
例如:一次跳出两层或多层循环。 多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
…
error:
if(disaster)// 处理错误情况
- 小练习:设计关机程序:功能要求为系统在1分钟后关机,如果输入:我是猪则取消关机!
#include<stdio.h>
#include<stdlib.h>//system()的头文件
#include<string.h>//strcmp()的头文件
//1.用go to语句实现
int main()
{
char input[20] = { 0 };
system("shutdown -s -t 60");
again:
printf("请注意,你的电脑在60秒内关机,如果输入:我是猪,就取消关机\n");
scanf("%s", input);
if (strcmp(input, "我是猪") == 0)
{
system("shutdown -a");
}
else
{
goto again;
}
return 0;
}
//2.直接用循环来实现
int main()
{
char input[20] = { 0 };
system("shutdown -s -t 60");
while (1)
{
printf("请注意,你的电脑在60秒内关机,如果输入:我是猪,就取消关机\n");
scanf("%s", input);
if (strcmp(input, "我是猪") == 0)
{
system("shutdown -a");
break;
}
}
return 0;
}
本章节总练习
- 练习一:计算n的阶乘
#include<stdio.h>
int main()//计算 n的阶乘
{
int i = 1;//1*2*3...*n
int n = 0;
int ret = 1;
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
ret = ret * i;
}
printf("%d\n", ret);
return 0;
}
- 练习二:计算 1!+2!+3!+……+10!
//法一:
#include <stdio.h>
int main()//计算 1!+2!+3!+……+10!
{
int i = 1;
int n = 0;
int ret = 1;
int sum = 0;
for (n = 1; n <= 10; n++)//有连续数的同一种计算时,参考这种形式的for循环
{
ret = 1;
for (i = 1; i <= n; i++)
{
ret = ret * i;
}
sum = sum + ret;
}
return 0;
}
//法二:
#include <stdio.h>
int main()
{
int n = 0;
int ret = 1;
int sum = 0;
for (n = 1; n <= 3; n++)
{
ret = ret * n;
sum = sum + ret;//直接把上一个阶乘的结果加上就好
}
printf("%d\n", sum);
return 0;
}
- 练习三:在一个有序数组中查找具体的某个数字n:二分查找法/折半查找法
//法一:挨个儿找,数据少时尚可,但如果数据很多,这种方法就会很慢
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 0;
int i = 0;
scanf("%d", &k);
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
if (arr[i] == k)
{
printf("找到了,下标是:%d\n", i);
break;//找到了就可以跳出循环了
}
}
if (i == sz)
{
printf("找不到\n");
}
return 0;
}
//法二:二分查找,折半查找
#include<stdio.h>
int main()//在一个有序数组中查找具体的某个数字n
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 0;
scanf("%d", &k);
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);//计算数据的个数
int left = 0;//定义left给它赋值第一个数的下标
int right = sz - 1;//定义right给它赋值最后一个数的下标
while (left <= right)//
{
int mid = (left + right) / 2;//定义mid给它赋值中间那个数的下标
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;
}
***二分查找,强化练习
#include<stdio.h>
int main()
{
int arr[] = { 2,4,7,9,13,16,26,47,69,78,99 };
int sz = sizeof(arr) / sizeof(arr[0]);
int k = 0;
scanf("%d",&k);
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;//mid必须放在循环里面啊!!!
//改进:int mid=left+(right-left)/2;
if (k > arr[mid])//因为整型能表示数据的大小是有限制的,left+right的大小可能会因为大于整型变量所容纳的最大数值而溢出,最终导致求得mid不准确
{
left = mid + 1;//为了解决这一问题,我们这样来表示:mid=left+(right-left)/2,这样就不存在溢出的问题了!!
}
else if (k < arr[mid])
{
right = mid - 1;
}
else
{
printf("找到了!他的下标是:%d", mid);
break;//可能第一次就会遇到k=arr[mid]的情况,所以当然要放到里面!!!
}
}
if (left > right)
{
printf("找不到!");
}
return 0;
}
- 练习4:编写代码,演示多个字符从两端移动,向中间汇聚。
//1.
//##################这个是这一题最终想要实现的效果
//w################!
//we##############!!
//wel############!!!
//.....
//welcome to bit!!!!
#include<stdio.h>
#include <string.h>
int main()// 编写代码,演示多个字符从两端移动,向中间汇聚。
{
char arr1[] = "welcome to bit!!!!";
char arr2[] = "w################!";
int left = 0;
int right = strlen(arr1) - 1;
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);//这种打印方式是直接打印没有很直观的动态展示向中间汇聚,
left++; //这是因为每句代码执行时间间隔小,可以通过Sleep()函数来增大时间间隔,具体见下段代码
right--;
}
return 0;
}
//2.
#include<stdio.h>
#include <string.h>
#include <windows.h>//注意使用Sleep()函数时要引用其头文件
int main()// 编写代码,演示多个字符从两端移动,向中间汇聚。
{
char arr1[] = "welcome to bit!!!!";
char arr2[] = "w################!";
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);//Sleep()函数,1000代表间隔为1000ms
left++;//其实还有一种更好的动态展示形式就是在一行上实现向中间汇聚的动态效果
right--;//此时只要将每一次打印的内容清空即可,具体见下段代码
}
return 0;
}
- 练习五:编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则 提示登录成,如果三次均输入错误,则退出程序。)
#include <string.h>
int main()
{
char password[20] = { 0 };
int i = 0;
for (i = 0; i < 3; i++)
{
scanf("%s\n", password);//这里不需要取地址,数组名本身就是地址!!!
if (strcmp(password, "abcdef") == 0)
{
printf("登陆成功");
break;
}
else
printf("密码错误\n");
}
if (i == 3)
printf("程序锁定!");
return 0;
}
- 练习六:用一些新的函数结合分支语句和循环语句实现猜数字游戏
一些基础知识:
1.system()是一个库函数,可以执行系统命令
2.rand()函数是用来生成一个随机数的函数,它不需要参数,返回类型是整型,返回的是一个随机的整数,范围是0-RAND-MAX(7FFF(十六进制)即32767),这个时候我们发现每一次新开一把游戏,它产生的随机数大小顺序是一模一样的,这时候我们意识到生成随机数的方法有问题,调用rand()函数的时候,需要先调用srand()来设置我们随机数的生成器,即每次开始游戏之前都要利用它来给rand()设置一个开始的起点,从而达到每一次游戏生成的随机数不同。
3.srand():设置一个随机数开始的起点,void srand(unsigned int seed)意思是srand需要的的参数是一个无符号整型类型的数值,假设先用srand(100),看到出来的数值都是一样的,原因是参数没有变,一直是100,换成srand(200),就是另一个数值在重复,也就是只要这个seed在变,生成的随机数就会变,那怎么让seed这个数字时时刻刻发生变化呢? 时间!!! 那我们怎么获得时间呢?
4.接下来就引出了时间戳的概念:时间戳就是某个时间点相对于计算机起始时间的差值,单位是秒,只要时间在流逝,时间戳就在改变。
那我们怎么生成一个时间戳呢?在我们的C语言中有一个time()函数,time函数返回的就是一个时间戳,它是用来获取一个系统时间的,time_t time(time_t*timer),这个函数需要一个是指针的参数,如果你不想要这个指针,那就传给他一个空指针,time_t是C语言给的一个类型,查看释义发现
5.typedef _time64_t time_t,就是说它实质上是一个64位的整型,所以他可以直接传给srand,而time的参数就可以用一个空指针NULL来代替,又因为time函数类型就是一个time_t,而srand需要的是一个unsigned int类型,因此可以把它强制转换一下,即srand((unsigend int) time(NULL));
!!!注意,这个时候不要忘记引用srand()的头文件#include <stdlib.h>和time()的头文件#include <time.h>
6.strcmp,判断两个字符串是否一样,一样就是==0,头文件是string.h
#include <stdio.h>
#include<stdlib.h>
#include <time.h>
void menu()
{
printf("*****************************\n");
printf("******** 1. play ********\n");
printf("******** 0. exit ********\n");
printf("*****************************\n");
}
//0~RAND_MAX(32767):产生随机数的范围
void game()
{
int guess = 0;
//1. 生成随机数
//0~99 --> 1~100
int ret = rand() % 100 + 1;//生成随机数的函数
printf("%d\n", ret);
//2. 猜数字
while (1)
{
printf("请猜数字:>");
scanf("%d", &guess);
if (guess < ret)
{
printf("猜小了\n");
}
else if (guess > ret)
{
printf("猜大了\n");
}
else
{
printf("恭喜你,猜对了\n");
break;
}
}
}
//指针
//int *p = NULL;NULL相当于空指针,类比int a=0;
//
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;
}