分支语句 if Switch 循环语句 while for do while goto语句
1.什么是语句?
C语言中有5类语句:表达式语句,函数调用语句,控制语句(分支和循环都属于控制语句),复合语句,空语句
控制语句:用于控制程序的执行流程,以实现程序的各种结构方式(C语言是结构化的程序设计语言,三种结构:顺序,选择,循环)C语言中有9种控制语句:
- 分支语句:if Switch
- 循坏执行语句:while for do while
- 转向语句:break语句,goto语句,continue语句,return语句
2.分支语句(选择结构)
if语句
语法结构:
单分支:if(表达式)语句; //表达式为非0(真),语句就要执行,为假就不执行语句
#include <stdio.h>
int main()
{
if (3 == 5)
printf("hehe\n"); //表达式为假不执行输出语句
int a = 10;
if (a > 5)
printf("haha\n"); //表达式为真执行输出语句
return 0;
}
双分支:if(表达式)语句1; else 语句2;//表达式为真,执行语句1;表达式为假,执行语句2
#include <stdio.h>
int main()
{
int score = 95;
if (score > 85)
printf("录取\n"); // if语句后默认只能跟一条语句
else
printf("不录取\n");
if (score > 60)
{
printf("合格\n"); //跟多条语句用{}(代码块)
printf("录取\n");
}
else
printf("不录取\n"); //else语句同理,默认只能跟一条语句
//跟多条语句用{}(代码块)
return 0;
}
多分支:if(表达式1)语句1; else if(表达式2)语句2; else 语句3;//那个表达式结果为真就执行那个语句,都为假则执行else下的语句
#include <stdio.h> //容易出错的地方
int main()
{
int score = 50;
if (60 < score < 70) //不能写成这种形式,score=50 60<50 为假:0 0<70 为真 执行语句
printf("合格\n"); //不在范围却执行输出
return 0;
}
#include <stdio.h>
int main()
{
int score=0;
scanf("%d",&score);
if (score < 60) //多分支(只选择一个分支执行语句)
printf("不合格\n");
else if (score >= 60 && score < 70)
printf("合格\n");
else if (score >= 70 && score < 80)
printf("良好\n");
else if (score >= 80 && score < 90)
printf("中等\n");
else
printf("优秀\n");
return 0;
}
代码风格
例题:悬空else
#include <stdio.h>
int main()
{
//悬空else
int a = 0; //代码风格不好
int b = 2;
if (a == 1)
if (b == 2)
printf("hehe\n");
//else与离得最近的一个if匹配,即使写成与最外面if对齐,也不是与最外面的if匹配
else
printf("haha\n");
//所以运行后没有输出内容
return 0;
}
if (a == 1) //规范写法
{ //这里{ }加了更方便理解,也可以不加(一条语句)
if (b == 2) // if与else 合起来是一条语句
printf("hehe\n");
else // else与离得最近的if匹配,跟对齐无关
printf("haha\n");
}
if (a == 1)
{
if (b == 2)
printf("hehe\n");//加了{}里面if和else无法匹配
} //else只能与外部if匹配
else
printf("haha\n"); //输出haha
注意:
- if else两个合起来是一条语句,所以上面那个例题中最外面的if语句下跟一个if else语句,不用加{ }
- 代码风格要规范:变量命名要有意义有规范
int a;//代码行数太多,a,b,c这种无意义的命名不方便理解阅读
char firstname//这种写法不规范,可读性不高
char FirstName//下面两种写法更规范,可读性更高
char first_name
推荐书籍: 《高质量C++/C编程指南》
例题:if书写形式对比
//写法1
if(condition){ //条件满足,return x;
//条件不满足不会执行语句直接跳到return y;
return x; //写法1和写法2意思是一样的,但写法1容易误解,代码风格不好
}
return y;
//写法2
if(condition)
{
return x;
}
else
{
return y;
}
//实例1:
int test()
{
int a = 3; //不好的写法
if (a == 3)
return 1;
return 0;
}
#include <stdio.h>
int main()
{
int r=test();
printf("%d\n",r);
return 0;
}
修正:
int test()
{
int a = 3; //好的写法
if (a == 3)
return 1;
else
return 0;
}
注意:两种写法是一个意思,但是写法一容易误解,写法二更通俗易懂
例题:=与==(好的代码风格可以避免一些错误)
//实例2:
#include <stdio.h>
int main()
{
int num = 3;
if (num == 5) //容易将==比较大小的符号写成=赋值
//即num=5,那么表达式一定为真,无论num等不等于5都会执行输出语句
//这种错误编译器检测不出来
printf("hehe\n");
int num1 = 3;
if (5 == num1) //写成下面这种方式,如果将==写成=编译器就会报错,避免了一些错误
printf("haha\n"); //常量与变量比较时,将常量放在左边(变量可以赋值)
return 0;
}
练习:
例题:判断一个数是否为奇数
//判断一个数是否为奇数
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
//判断
if (num % 2 == 1) //此处没有必要倒过来写
printf("奇数\n"); //表达式和常量,表达式变量无法赋值,所以无需将常量放在左边
else
printf("偶数\n");
return 0;
}
例题:输出1~100之间的奇数
//输出1~100之间的奇数
#include <stdio.h>
int main()
{
//输出1~100之间的奇数
//产生1~100的数 while
//判断输出1~100的奇数
int num = 1;
while (num <= 100)
{
if (num % 2 == 1)
printf("%d\n", num);
num++;
}
//法2
int i = 1;
while (i <= 100)
{
printf("%d\n", i);
i += 2;
}
return 0;
}
Switch语句
也是一种分支语句,通常用于多分支的情况(if用于多分支不简洁,太复杂)
语法结构:
Switch(整型表达式)
{
语句项;
}
语句项如下:
case 整形常量表达式:
语句;
注意:整型表达式值结果是整型,据表达式值与语句项进行匹配,语句项又是case表达式
例题:输入数字输出对应星期数
#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: //语句项中不加break
printf("星期三\n"); //输入3会打印输出星期三...星期日
break; //只给了程序入口,未告知程序出口,会一直往后走完最后一个语句项停止
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期日\n");
break;
}
return 0;
}
注意:Switch后面表达式必须是整型不可以是浮点型,case后表达式必须是整型常量,不能是浮点型或者变量,case后面表达式可以跟字符常量(字符存储时存的是ASCII,ASCII本质上也是整数,也属于整型)
例题变形:1~5打印输出weekday 6~7打印输出weekend
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1://case后面可以不跟break语句,没有break语句会持续往下走
case 2://可以实现多个case语句匹配同一个执行语句
case 3:
case 4:
case 5:
printf("weekday\n");
break;
case 6://执行语句相同时可以简化代码
case 7:
printf("weekend\n");
break;//最后一个break貌似可以不加,但是建议加上,以防后面再加case语句出错
default:
printf("选择错误\n");
break;
}
return 0;
}
default子句
输入错误没有匹配的case语句项,从default进入并跳出Switch语句,可以给出提示性语句
例题 打印输出的m=?n=?
#include <stdio.h>
int main()
{
int n = 1;
int m = 2;
switch (n)
{
case 1:
m++;
case 2:
n++;
case 3:
switch (n) // switch允许嵌套使用
{
case 1:
n++;
case 2:
m++;
n++;
break; //此处的break是从里面的Switch跳出到外面的Switch
} //外面的Switch在case 3没有break还会继续往后执行case 4
case 4:
m++;
break;
default:
break;
}
printf("m =%d,n=%d\n", m, n);// m=5,n=3
return 0;
}
注意:
- Switch是可以嵌套的
- Switch中break语句只能跳出自己所在的Switch,不是一个break语句跳出所有的Switch语句
3.循环语句
while语句 for语句 do while语句
while语句
语法结构:
while(表达式)//表达式为真,执行循环语句,执行完再判断,直到表达式为假退出循环
循环语句;
例题:生成1—10的数字
//打印1~10数字
#include <stdio.h>
int main()
{
int i = 1;
while (i < 11)
{
printf("%d\n", i);
i++;
}
return 0;
}
while循环执行流程:
先判断表达式,表达式为真,执行循环语句,执行完后再次判断表达式真假,为真就继续执行循环语句,当有一次表达式为假就停止循环
while循环中break和continue
//循环中break和continue的意思
//实例1
#include <stdio.h>
int main()
{
int i = 1;
while (i < 11)
{
if (5 == i)//只打印1~4
break; //break是作用于循环的,而不是作用于if语句,所以将if下的break用{}扩起来还是会终止循环
printf("%d\n", i);
i++;
}
return 0;
}
//实例2:
#include <stdio.h>
int main()
{
int i = 1;
while (i < 11)
{
if (5 == i) // 打印1~4 光标一直在闪动(死循环)
continue; // continue被执行会跳过本次循环continue后面的代码
// 直接到判断部分判断要不要进行下次循环
printf("%d\n", i);
i++;
}
return 0;
}
//实例2的变形
#include <stdio.h>
int main()
{
int i = 1;
while (i < 11)
{
i++;
if (5 == i)
continue; //输出2,3,4 6,7,8,9,10,11
//只跳过了5没有输出,执行了continue语句跳过本次循环continue后面的代码,重新判断能否进行下次循环
//以后每次循环都不满足5==i,所以continue只执行一次
printf("%d\n", i);
}
return 0;
}
注意:
- break的作用是终止整个循环,应用场景:某种条件下终止循环
- continue的作用是跳过本次循环continue后面的代码,直接到判断部分判断是否进行下次循环,可能会导致代码死循环
- 区别:break是用于永久的终止循环,continue是跳过本次循环后面的代码,直接去判断部分,进行下次循环的判断
与while循环有关的代码
代码1
//这里的代码适当修改是可以用來清理缓冲区的.
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF) // getchar()获取字符的ASCII值
putchar(ch); //打印字符 方法二printf("%c\n",ch);
return 0; // end of file 文件结束标志(值=-1 整数)
}
// getchar读取出错/遇到文件末尾的返回值为EOF,读取正确返回读取字符
// EOF就是-1 如果定义一个字符型变量(1byte),无法接收存储-1这个返回值
//所以定义一个整型去接收getchar()的返回值
//读到正常字符就打印输出 停止的方法(getchar返回EOF):Ctrl+z
//输入缓冲区存在于getchar和键盘之间的,当输入字符'a'时,敲入回车才会被放入输入缓存区
//因此输入缓冲区有'a''\n'两个字符,getchar依次去读取并进入循环输出
//显示效果是:输出字符后还换行显示光标
输入a 打印输出a 后换行显示光标,说明输入缓冲区的有'a''\n'两个字符
代码1的变形:清理缓冲区
//举一个例子
//假设密码是一个字符串
#include <stdio.h>
int main()
{
char password[20] = {0}; // password是数组名,数组名本来就是地址所以scanf这里不用加取地址
printf("请输入密码:"); // abcdef\n(敲了回车键才能到输入缓存区被scanf获取)
scanf("%s", password); // scanf只取abcdef放进password,不会获取\n(留在输入缓存区)
//getchar(); //不好的改进:读取\n 使输入缓存区变空(临时写法不是完全正确)
//当输入abcdef defghi\n时,scanf只会读取空格前的字符
//所以输入缓存区还剩 defghi\n getchar()只能读取一个字符 此处getchar读取一个空格
//输入缓存区中还剩有defghi\n,下一个getchar()直接读取字符,不用等待 打印输出:No
//此处的加一个getchar()改进代码完全失效
int ch = 0;//好的改进:清空缓存区
while ((ch = getchar()) != '\n')//直到读取到最后一个字符\n表达式为假,退出循环
{
;
}
printf("请确认密码:Y/N");
int ret = getchar(); //取走了输入缓存区中的\n
if ('Y' == ret) //\n!='Y'执行else语句打印输出 No
{
printf("Yes\n");
}
else
{
printf("No\n");
}
return 0;
}
不加清理缓冲区的语句时,直接不要你输入选择就直接输出No
不好的改进,只用一次getchar(),无法应对缓冲区有多个字符的情况
代码2:只打印数字字符,跳过其他字符
//代码功能:只打印数字字符,跳过其他字符
#include <stdio.h>
int main()
{
char ch = '\0';
while ((ch = getchar()) != EOF)
{
if (ch < '0' || ch > '9')
continue;
putchar(ch);
}
return 0;
}
输入字符a、q都被跳过了不打印输出,输入数字4,打印输出
输入abcde1234wer,跳过了字符不打印只打印数字
for语句
for循环的形式是将初始化,判断,调整三个部分放在一起,比较集中,容易控制循环。因此for循环用的最普遍,最方便。
语法结构:
for(表达式1,表达式2,表达式3)
循环语句;
表达式1:初始化部分(初始化循环变量) 表达式2:条件判断部分(判断循环何时终止) 表达式3:调整部分(循环条件的调整)
例题:打印1~10
//for循环打印1~10
#include <stdio.h>
int main()
{ //打印1~10
int i = 0;
for (i = 1; i <= 10; i++)
{
printf("%d", i); //单条语句可以不加{},两条及以上必须加{}
}
return 0;
}
for循环执行流程:
exp1:初始化部分 exp2: 判断部分 exp3:调整部分
先执行初始化部分,再执行判断部分,判断如果为非0(即表达式结果为真)就执行循环体部分,执行完循环体就进入执行调整部分,调整完之后再进行判断,表达式结果一直为真,就一直这样循环,当有一次判断为假时,就跳出循环。
for循环中的break和continue
#include <stdio.h>
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (5 == i)
// break; //跳出循环,终止循环
continue; //跳过continue本次循环后面的代码来到调整部分
//与while中不同,while中的continue时跳过本次循环后面的代码来到判段部分
printf("%d", i); //输出1,2,3,4 break
//输出1,2,3,4,6,7,8,9,10 continue
}
return 0;
}
注意:break 永久终止循环(结束所在的循环),continue 跳过本次循环后面的代码直接跳到for循环的调整部分
for语句的循环控制变量
建议:
- 不可在for循环的体内修改循环变量(在调整部分修改),防止循环失控
- for语句的循环控制变量取值采用 前闭后开区间 写法
两种写法都可以打印出数组中所有元素,但是更推荐第一个写法(前闭后开区间 )第一个代码10 就是循环次数,可读性更高,写9没有10好,没有意义,只是更推荐使用第一个写法,但在某些场景不适合,eg
#include <stdio.h>
int main()
{ //打印100~200
int i = 0;
for (i = 100; i <= 200; i++)//为了可读性更高没有写成前闭后开区间
{ //所以写法一不是绝对的,在一些场景也不适用
printf("%d\n", i);
}
return 0;
}
for循环的变种
变种1:死循环问题和省略
#include <stdio.h>
int main()
{
//代码1 死循环的问题和省略初始化
for (;;)
{
printf("hehe\n");
//死循环一直输出hehe,原因是for循环中的判断部分都省略了
}
// for循环的判断部分省略意味着判断会恒成立,所以会死循环(初始化和调整部分不影响死循环)
// for循环中的初始化部分,判断部分,调整部分是可以省略的, 但是不建议初学时省略,容易导致问题
return 0;
}
省略初始化:
#include <stdio.h>
int main()
{ //省略初始化
int i = 0;
for (/*i = 0*/; i < 10; i++)
{
printf("haha\n"); //省略后仍然可以打印输出10个haha
}
return 0;
}
#include <stdio.h>
int main()
{//省略初始化出问题了
int i = 0;
int j = 0;
for (/*i = 0*/; i < 3; i++)
{
for (/*j = 0*/; j < 3; j++)
{
printf("gaga\n"); //不省略for循环初始化部分打印9个gaga
//省略 只打印3个gaga
//原因是当省略初始化时,先进入外部循环判断部分0<3,执行循环语句
//进入内部循环判断部分0<3,执行循环语句,循环输出3个gaga后退出内部循环,此时j=3
//进入外部循环调整部分i++,进入外部循环的判断部分i=1<3,再次进入内部循环判断部分
//但是此时j=3(无初始化部分,不进行初始化),不满足<3,不进入内循环执行语句
//此后i=2与i=1同理 i=3退出外循环
}
}
return 0;
}
一种写法的说明:
#include <stdio.h>
int main()
{ //一种写法
for (int i = 0; i < 10; i++)
{
printf("guagua\n"); //也可以正常输出10个guagua
// C语言:C99语法中才支持这种写法,不支持C99的编译器无法正常编译
// C++:支持这种写法
}
return 0;
}
变种2:使用多个循环变量控制循环
#include <stdio.h>
int main()
{ //代码2 使用多个循环变量控制循环
int x, y;
for (x = 0, y = 0; x < 2 && y < 5; ++x, y++)
{
printf("hehe\n");
}
return 0;
}
例题:判断下面循环会循环几次
#include <stdio.h>
int main()
{
int i = 0;
int k = 0;
for (i = 0, k = 0; k = 0; i++, k++) //循环0次
//原因:判断部分不是判断语句而是赋值语句
//判断部分将k赋值为0,表示式结果为0,0为假,不进入循环
{ //变形:判断部分 k==0 循环1次
k++;
}
return 0;
}
do while语句
语法结构
do
循环语句;
while(表达式);
特点:先执行循环语句再判断表达式(要不要继续进行),循环体至少执行一次
执行流程:
先执行循环体语句,再判断表达式真假,表达式为真(不为0),就继续执行循环语句,表达式如果为假就停止
例题:打印1~10
#include <stdio.h>
int main()
{ //打印1~10
int i = 1;
do
{
printf("%d ", i);
i++;
} //循环体中多条语句需要加{}
while (i <= 10);
return 0;
}
do while语句中的break和continue
#include <stdio.h>
int main()
{
int i = 1;
do
{
if (i == 5)
// break;
continue;
printf("%d ", i); // i++放在循环体最后一行 输出:1,2,3,4 break
// i++放在循环体第一行 输出:2,3,4 break
// break跳出循环,让循环整体终止
// i++放在循环体第一行 输出2,3,4,6,7,8,9,10,11 continue
// i++放在循环体最后一行 输出 1,2,3,4 死循环了 continue
// continue:跳过continue后面的代码,来到判断部分
i++;
}
while (i <= 10);
return 0;
}
do while语句的特点
循环至少执行一次,使用场景有限,不经常使用(用的多到少:for while do while)
循环的练习题
例题1:计算n的阶乘
#include <stdio.h>
int main()
{ //计算n的阶乘 1*2*3*...*n
int i = 1;
int n = 0;
int ret = 1; //不考虑溢出问题
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
ret = ret * i;
}
printf("%d\n", ret);
return 0;
}
例题2:计算1!+2!+3!+...+10!
//两层for循环,效果差 每个阶乘都是从1开始计算
#include <stdio.h>
int main()
{ //计算1!+2!+3!+...+10!
int i = 1;
int j = 1;
int ret = 1;
int sum = 0;
for (i = 1; i <= 10; i++)
{
ret = 1; //不重新赋值会产生累积值导致结果错误
for (j = 1; j <= i; j++)
{
ret = ret * j;
}
sum = ret + sum;
}
printf("%d\n", sum);
return 0;
}
更好的做法
1! 1 2! 1*2=1!*2 3! 2!*3 没有必要重复计算(即:从1开始计算)
#include <stdio.h>
int main()
{ //计算1!+2!+3!+...+10!
int i = 1;
int ret = 1;
int sum = 0;
for (i = 1; i <= 10; i++)
{
ret = ret * i;//利用前面计算的结果 一层for循环,效率更高
sum = ret + sum;
}
printf("%d\n", sum);
return 0;
}
例题3:在一个有序数组中查找具体的某个数字n。
//顺序查找
#include <stdio.h>
int main()
{ //在一个有序数组中查找具体的某个数字n
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; //没有利用数组的有序性
int k = 7; //要找数字7
int i = 0;
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;
}
更好的做法:
二分法 效率更高(查找一次少一半数据,数据越多效率越高)(二分法的前提条件:有序的)时间复杂度是log2n(n是有序数组中元素个数)eg: 有232个有序数字只查找32次 效率很高
#include <stdio.h>
int main()
{ //有序数组找具体数字 二分查找法 效率高
// 1.找中间元素下标mid (找出数组左边元素下标和右边元素的下标 中间元素下标是最左边元素left和最右边元素right下标的平均值)
// 2.比较,不相等就修改right或者left(修改范围)
// eg:arr[]={1,2,3,4,5,6,7,8,9,10}中找7
//(0+9)/2=4 整数除法 中间元素下标为4 arr[4]=5<7(要找的数字7在5的右边) 查找一次,查找范围缩小一半(数据少了一半)
//右下标不变,左下标变成mid+1 新范围:5~9 (重复1,2步骤直至找到)
//新中间元素下标 mid=(5+9)/2=7 arr[7]=8>7(要找的数字7在8的左边) 左下标不变 右下标变成mid-1 新范围:5~6
//新中间元素下标 mid=(5+6)/2=5 arr[5]=6<7(要找的数字7在6的右边) 右下标不变,左下标变成mid+1 新范围:只有下标6了(左右下标重合)
// arr[6]=7 找到数字7 如果此时arr[6]!=7 就找不到数字7了
//顺序查找:最坏情况查找10次 二分查找最坏情况是要查找的元素可能位于数组的边缘位置,比如第一个或最后一个位置,或者数组中不存在要查找的元素。
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int k = 7; //要找数字7
int sz = sizeof(arr) / sizeof(arr[0]);
int left = 0;
int right = sz - 1;
while (left <= right) // left<=right 还有元素进行查找
//等号不能漏掉,不然会出错
{
int mid = (left + right) / 2;//放在循环内部,要多次求mid
//left和right非常大,加起来可能会溢出,此时mid就不对了
//解决:left+(right-left)/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) // left>right 没有元素进行查找 找不到了
{
printf("找不到了");
}
return 0;
}
例题4:编写代码,演示多个字符从两端移动,向中间汇聚
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
char arr1[] = "welcome to Beijin!";
char arr2[] = "##################";
int left = 0;
int right = strlen(arr2) - 1;
// strlen求字符串长度(\0前的所有字符)最右侧下标:字符串长度-1
//第二种做法:int right = sizeof(arr1)/sizeof(arr1[0])-2;
// sizeof获取变量或类型所占字节数 数组总体占的字节/单个数组所占字节 =数组元素(\0占空间但是不算长度)
//所以最右侧下标=数组元素-2(减去\0和下标从0开始算要减1)
while (left <= right) // left<=right 还有元素未替换
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(1000); //延迟1s 使变化肉眼可见
// Sleep(1000)是Windows系统下的函数,它可以使程序暂停执行一段时间,单位是毫秒。要使用需要包含头文件windows.h。
system("cls"); // system是一个库函数, 可以执行系统命令
// system("cls")清空屏幕 要使用这个指令需要包含头文件stdlib.h
//用了这一行可以在一行显示
left++; //调整部分 向中间汇聚
right--;
}
printf("%s\n", arr2);
//最后的结果显示,不写最后哪一行显示后延时1s会被清屏
return 0;
}
例题5:编写程序实现,模拟用户登录场景,并且只能登陆三次(只允许输入三次密码,如果密码正确则提示登陆成功,如果连续三次都输入错误,则退出程序)
#include <stdio.h>
#include <string.h>
int main()
{
//比较2个字符串是否相等,不能使用==,而应该使用一个库函数: strcmp
//如果返回値是0,表示2个字符串相等
//使用这个函数应该包含string.h头文件
int i = 0;
char password[20] = {0};
//假设密码是字符串: abcdef
for (i = 0; i < 3; i++)
{
printf("请输入密码:>");
scanf("%s", password); //数组本身就是一个地址所以不用加&
if (strcmp(password, "abcdef") == 0) //有一个容易犯得错:password==“abcdef"
{
printf("登录成功\n");
break;//跳出循环
}
else
{
printf("密码错误\n");
}
}
if (i == 3)//登录成功和三次输入错误都会跳出循环所以加个if语句
//登录成功i<3不会执行语句
{
printf("三次密码输入错误,退出程序\n");
}
return 0;
}
例题6:猜数字游戏实现
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void meau()
{
printf("*************************\n");
printf("*********1.play**********\n");
printf("*********0.exit**********\n");
printf("*************************\n");
}
void game() //电脑产生一个随机数(1~100) 猜数字 反馈:猜大了/猜小了
{
int guess = 0;
// srand(100);
//又出问题了,srand中数值不变,对应随机数也不变 srand中数值变化导致随机数变化 srand中需要一个变化的值 那放一个随机数可以吗? 不可以,不现实
//时时变化的值:时间 时间获取:时间戳(当前时间相对于计算机起始时间之间的差值 单位:s 只要时间在变化,时间戳就会变化),时间戳是一个变化的整数 满足srand()的需求
// 时间戳获取:time() time_t time(time_t *timer) 返回一个时间戳(获取系统时间) 参数是一个指针 可以传一个NULL(空指针 跟整型初始化为0类似 相当于0 (void*) 0) 会返回time_t的值(即时间戳)
// time_t 是C语言中给的类型 本质上是一个整型类型 srand中参数需要一个无符号整型 所以可以将time()的返回值作为srand的参数 但是需要进行强制类型转换(time_t--->unsigned int)
// srand((unsigned int)time(NULL)); // srand(),rand()需要加头文件stdlib.h time()需要的头文件time.h
//使用time()返回的时间戳作为这个随机数的生成起点的设置
//这里又有一个问题,当输入比较快时,会出现前后两个随机数相同的现象 解决:在调用rand()生成随机数前用srand()设置随机数起点不需要每次调用game()都重新设置,只需要在整个工程下设置一次即可 因此只需要放入主函数即可
// 1.生成随机数
int ret = rand() % 100 + 1;
//生成随机数的函数 int rand (void);不需要参数,返回类型为整型(返回一个0~RAND_MAX的随机整数) RAND_MAX 0x7fff即32767
// printf("%d\n", ret);
//发现每次关闭程序再次运行时生成随机数与上次都一样 解决:使用srand()设置随机数的起点(调用rand函数前使用srand设置随机数的生成器)
// srand设置随机数的起点 : void srand(unsigned int seed); 内部需要一个无符号整型数
//现在又出现一个新问题:需要生成1~100的随机数 解决:rand()%100+1:余数是0~99 + 1----->1~100
// 2.猜数字
while (1)
{
printf("请猜数字:>");
scanf("%d", &guess);
if (guess < ret)
{
printf("猜小了\n");
}
else if (guess > ret)
{
printf("猜大了\n");
}
else
{
printf("恭喜你,猜对了\n");
break; //直到猜对跳出循环
}
}
}
int main()
{
srand((unsigned int)time(NULL)); //设置随机数起点
int input = 0;
do
{
meau();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
// printf("猜数字\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
在使用rand()前未用srand()设置随机数的起点,发现每次关闭程序重新开始游戏出现的 随机数都是一样的
给srand()一个定值eg 100,200 无法产生变化的随机数
将srand()放在game()中,每次调用都重新设置随机数起点,导致输入太快时前后两次生成随机数相同 解决:将srand()放入 main()只在整个工程中设置一次随机数起点
将srand()放入main()中,生成的随机数就很随机了
需要生成的随机数范围在1~100: rand()%100+1
4.goto语句
随意滥用的有跳转功能的语句 (建议少用goto语句:随意乱跳,打乱程序原有执行流程,容易出错)
用法:
#include <stdio.h>
int main()
{
again:
printf("hehe\n");
printf("haha\n");
goto again;//again 是个符号 跳转到符号again所在位置 实现死循环
return 0;
}
goto语句不能跨函数跳转,只能在同一个函数内部跳转
#include <stdio.h>
void test()
{
again:
}
int main()
{
printf("hehe\n");
printf("haha\n");
goto again; //err
return 0;
}
应用场景:终止程序在某些深度嵌套的结构的处理过程
三层for循环用一次goto语句就可以跳转出最外层循环,用break语句要跳转3次才可以。
例题:关机程序,电脑运行起来后一分钟内关机;如果输入:不要关机,就取消关机
用goto实现
#include <stdio.h>
#include <string.h> //strcmp()
int main()
{ //关机程序
//通过在命令行窗口输入命令关机 shutdown -s -t 60 (-s关机 -t设置时间倒计时关机 60表示60秒内关机)
// shutdown -a 取消关机 在C语言中用库函数system()来执行系统命令
char input[20] = {0};
system("shutdown -s -t 60");
again:
printf("请注意你的电脑在60秒内关机,如果输入:不要关机,就取消关机\n");
scanf("%s", input);
if (strcmp(input, "不要关机") == 0) //比较两个字符串不用==,而是strcmp
{
system("shutdown -a");
}
else
{
goto again;
}
return 0;
}
不用goto实现
#include <stdio.h>
#include <string.h> //strcmp()
int main()
{ //关机程序
//通过在命令行窗口输入命令关机 shutdown -s -t 60 (-s关机 -t设置时间倒计时关机 60表示60秒内关机)
// shutdown -a 取消关机 在C语言中用库函数system()来执行系统命令
char input[20] = {0};
system("shutdown -s -t 60");
while (1)
{
printf("请注意你的电脑在60秒内关机,如果输入:不要关机,就取消关机\n");
scanf("%s", input);
if (strcmp(input, "不要关机") == 0) //比较两个字符串不用==,而是strcmp
{
system("shutdown -a");
break;
}
}
return 0;
}