- 前言
- 分支语句(选择结构)
- if语句
- siwtch语句
- 循环语句
- while语句
- getchar()函数
- for语句
- do while语句
- goto语句
本章学习分支语句和循环语句
C语言是结构化的程序设计语言
结构有以下三种:顺序、选择、循环
语句:C语言中由一个分号;隔开的为一个语句 如 int i = 0;
语法结构:
①
if(表达式) 语句;
表达式结果为真,则执行语句;为假,不执行
C语言中0为假,非0值为真
如:
int main()
{
int age = 0;
scanf("%d", &age);
if (age < 18)
{
printf("未成年");
}
return 0;
}
②
if(表达式) 语句1; else(表达式) 语句2;
如:
int main()
{
int age = 0;
scanf("%d", &age);
if (age < 18)
{
printf("未成年");
}
else
{
printf("成年");
}
return 0;
}
③(多分支)
if(表达式1) 语句1; else if(表达式2) 语句2; : : else 语句n;
如:
int main()
{
int age = 0;
scanf("%d", &age);
if (age < 18)
{
printf("少年");
}
else if(18<=age&&age<30)
{
printf("青年");
}
else if(30<=age&&age<50)
{
printf("中年");
}
else if (50 <= age && age < 80)
{
printf("老年");
}
else
{
printf("老不死");
}
return 0;
}
- 注意事项
-①if条件判断的错误写法1
int age=10;
if(18<=age<30)
printf("中年");
其判断逻辑为:先判断18<=age,即18<=10为假,结果为0
再判断0<30为真,结果为1,则显示中年,而10岁明显不是中年,则错误
正确写法:if(18<=age&&age<30)
"&&“为逻辑操作符,意为"且”,同时满足才为真.
②if条件判断的错误写法2
int a= 1;
if (a = 2)//本意为判断a和2是都相等,实际将2赋值给a,条件为真,执行
{
printf("hehe");
}
return 0;
=:赋值操作符
==:关系操作符,测试"相等"
上述例子应改为if(a==2)
小技巧:写成if(2==a),这样若少写一个=,即if(2=a),则编译器会报错(变量不可赋值给常量)
③执行多条语句应用代码块{}括起来
if语句只能控制后面一条语句,若想控制多条语句,则用{}
int age = 10;
if (age > 18)
printf("成年");//10>18为假,不执行
printf("可以谈恋爱");//执行该条语句,证明该语句不受if控制
return 0;
显示:可以谈恋爱
④悬空else
int a = 0;
int b = 2;
if(a == 1)
if(b == 2)
printf("hehen");
else//else看上去似和第一个if匹配,实际不是
printf("hahan");
return 0;
//不显示任何结果
else匹配原则:与距离最近的未匹配的if进行匹配
规范写法:
int a = 0;
int b = 2;
if(a == 1)
if(b == 2)
printf("hehen");
else
printf("hahan");
return 0;
练习:
- 判断一个数是否为奇数
- 输出1-100之间的奇数
- 判断一个数是否为奇数
int main()
{
int n = 0;
scanf("%d", &n);
if (0 == n % 2)
{
printf("不是奇数");
}
else
{
printf("是奇数");
}
return 0;
}
- 输出1-100之间的奇数
int main()
{
int i = 1;
while (i <= 100)
{
if (1 == i % 2)
{
printf("%d ", i);
}
i++;
}
return 0;
}
//int i = 1; printf("%d ",i+=2);
siwtch语句
switch语句也是分支语句,常用于多分支的情况.比if…else if…else更优更简便
语法结构:
switch (整型表达式)
{
case 整型常量表达式:
语句;
}
switch常与break搭配使用,达到分支效果
如:
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;
case 4:
printf("星期四n");
break;
case 5:
printf("星期五n");
break;
case 6:
printf("星期六n");
break;
case 7:
printf("星期日n");
break;
default:
printf("输入错误");
}
}
说明:case为入口,break为出口.若无break,则会一直往下运行.
default:若case整型常量表达式均不满足,则执行default中的语句.default位置可在任意case语句的位置(习惯放在最后)
是否添加break由我们所想达到的效果决定.
上述例子,若我们想在输入1,2,3,4,5后,显示"工作日";输入6,7,显示"休息日".
则代码为:
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("输入错误");
}
}
建议不要漏break和default,养成良好的编程习惯
- switch可嵌套使用
includeint 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; } case 4: m++; break; default: break; } printf("m = %d, n = %dn", m, n); return 0; } //m=5,n=3
break只跳出一层所在的switch语句
循环语句 while语句语法结构:
while (表达式)
{
语句;
}
先来看下if和while的关系:
if (1)
{
printf("hello");
}
return 0;
//显示 hello
而将if改为while后:
while (1)
{
printf("hello");
}
return 0;
//显示死循环的hello
区别: 若判断条件为真,if语句只执行一次语句,而while语句会一直执行
- break在while语句中的作用
如:
int main()
{
int i = 1;
while(i<=10)
{
if (5==i)//当i==5时,break跳出后面所有的循环
break; //break:永久终止循环
printf("%d ",i);
i++;
}
return 0;
}
//显示结果:1 2 3 4
总结: 循环中遇到break,则终止后面所有的循环,直接结束循环.
while中的break作用是永久终止循环
- continue在while语句中的作用
如:
int main()
{
int i = 1;
while(i<=10)
{
if (5==i)
continue; //continue跳过本次循环,不会执行后面的代码,而是回到while的判断部分
printf("%d ",i);//continue跳过本次循环,i++没有执行(i没有调整),进入死循环状态
i++;
}
return 0;
}
//显示结果:1 2 3 4 死(空)循环
getchar()函数总结:continue终止本次循环,即continue后面的代码不会执行,直接跳到while的判断部分,进行下一次循环的入口判断.
getchar()函数:
①从缓冲区读取一个字符,返回类型为整型(字符型char本质是ASCII值,为整型)
②使用要包含头文件stdio.h
③getchar()函数不仅可以从输入设备获得一个可显示的字符,而且可以获得屏幕上无法显示的字符,如控制字符回车换行,空格等.
注意:1.因为getchar返回类型为整型,所以创建存放字符的变量的类型为整型,如int ch;
2.gecharr()读取失败会返回EOF(end of file:文件结束标志),值为-1(整型)
直接上代码:
int main()
{
int ch = 0;
char password[20] = {0};
printf("请输入密码>:");
scanf("%s",password); //数组名本身就是地址,所以不需要再取地址
printf("确认密码(Y/N)>:");
ch = getchar();
if(ch == 'Y')
{
printf("确认成功n");
}
else
{
printf("确认失败n");
}
return 0;
}
运行程序:在键盘输入密码123456,并敲下回车键,结果为确认失败.
程序分析:
①scanf函数读取123456,并不会读取n,后存到password数组.
②因为缓冲区余下n,所以getchar函数会直接读取n,存到ch变量,不会等待键盘输入.
③n==‘Y’,为假,则"确认失败".
解决方法:缓冲区余下n,导致不暂停(等待输入),则用getchar();清理缓冲区.
int main()
{
int ch = 0;
char password[20] = {0};
printf("请输入密码>:");
scanf("%s",password); //数组名本身就是地址,所以不需要再取地址
getchar();//清理缓冲区
printf("确认密码(Y/N)>:");
ch = getchar();
if(ch == 'Y')
{
printf("确认成功n");
}
else
{
printf("确认失败n");
}
return 0;
}
次方法不可取,应用范围有限:
当输入为:12345 abc(中间有空格),仍为"确认失败".
程序分析:
①缓冲区为12345 abcn.
②scanf函数遇到空格会停止.(不读取空格)
③getchar函数读取空格后,缓冲区还剩下abcn.
④下一个getchar函数读取a,a=='Y’为假,则"确认失败".
优化方法:上述方法只用了一次getchar,无法保证读取的字符一定是n.优化思路:用循环和空语句取走scanf读取字符后留在缓冲区的字符.
int main()
{
int ch = 0;
char password[20] = {0};
printf("请输入密码>:");
scanf("%s",password);
//清理缓冲区
while((ch = getchar()) != 'n')
{
;
}
//
printf("确认密码(Y/N)>:");
ch = getchar();
……
}
程序分析:getchar读取余下的字符,若ch!=’n’,什么都不做,只是取走,再回到判断部分,直到遇到n,n!=’n’为假,跳出循环.此时n已被取走,缓冲区清理完毕.
for语句语法结构:
for(表达式1; 表达式2; 表达式3)
循环语句;//(默认控制一条语句)
表达式1:初始化部分
表达式2:条件判断部分
表达式3:调整部分
和while语句相比:
①for语句三个部分集中,便于更改,优于while语句
②当使用continue时,while语句需要判断continue和调整语句的相对位置来决定是否执行调整语句;而for语句,则一定会执行调整语句.
int main()
{
int i = 0;
for(i=1;i<=10;i++)
{
if(5==i)//当5==i,continue跳出本次循环,接着执行i++
{
continue;
}
printf("%d",i);
}
}//输出结果为1234678910
- for语句的建议:
①不可在for循环体内改变循环变量
int main()
{
int i = 0;
for(i=1;i<=10;i++)
{
if(i = 5)
printf("%d",i);
}
return 0;
} //此时程序运行结果为死循环打印5
②建议初始化部分和条件判断部分采取"前闭后开"写法.
如for( i = 0;i < 10; i++),指明0到9,10个元素.
- for语句的变形:
变形①:省略初始化,条件判断,调整三部分
for (; ; )
{
printf("haha");
return 0;
}
//死循环打印haha
语法上可以省略,但当省略条件判断部分,那判断部分恒为真,进入死循环.
变形②:初始化,条件判断,调整三部分可以是一条语句,也可多条.
int x, y;
for (x = 0, y = 0; x<2 && y<5; ++x, y++)
{
printf("hehen");
}
return 0;
变形③:省略初始化部分
int main()
{
int i = 0;
int j = 0;
for( ; i < 10 ; i++)
{
for( ; j<10 ; j++)
{
printf("hellon");
}
}
}
程序分析:
①i=0且i<10进入内层循环,从j=0到j=10打印了10次hello后跳出内层循环.
②i++后i=1且i<10,但此时j=10(没有初始化),则后续i一直自增但不会再打印.
下面为关于for的一道面试题:判断循环的次数
int main()
{
int i = 0;
int k = 0;
for(i = 0 , k = 0 ; k = 0 ; i++ , k++)
{
k++;
}
return 0;
}
do while语句0次,k=0为赋值语句,为假,不执行.
语法结构:
do
{
循环语句;
}while(表达式)
do…while语句至少执行一次,使用场景有限,少用.
实例:
int main()
{
int i=1;
do{
printf("%d",i);
i++;
}while(i<=10);
return 0;
}//打印1-10
在do…while语句中使用continue,continue会跳过后面的语句来到while的判断部分(类似在while语句中),需要根据continue和调整部分的相对位置来决定是否执行.
练习:
1.计算n的阶乘
2.计算1!+2!+3!+……+10!
3.再一个有序数组中查找具体的某个数字n。编写int binsearch(intx,intv[],int n);功能:再v[ 0 ]<=v[1]<=v[2]<=…<=v[n-1]的数组中查找x
4.编写代码,演示多个字符从两端移动,向中间汇聚
5.编写代码实现,模拟用户登录情景,并且只能登录三次。(值允许输入三次密码,如果密码正确则提示成功,如果三次均输入错误,则退出程序)
1.计算n的阶乘
#includeint main() { int n, i, ret = 0; scanf("%d", &n); //方法一:while语法 //ret = 1; //i = 1; //while (i <= n) //{ // ret *= i; // i++; //} //printf("%d", ret); //方法二:for语法 // for (ret = 1, i = 1; i <= n; i++) // { // ret *= i; // } // printf("%d", ret); //方法三:do while语法 ret = 1; i = 1; do { ret *= i; i++; } while (i <= n); printf("%d", ret); return 0; }
2.计算1!+2!+3!+……+10!
#includeint main() { //方法一: //int n, i, j, ret = 0; //int sum = 0; //scanf("%d", &n); //for (i = 1; i <= 10; i++) //{ // ret = 1; // for (j = 1; j <= i; j++) // { // ret *= j; // } // sum += ret; //} //printf("%d", sum); //return 0; //方法一实现了阶乘求和,但每次阶乘都得从1开始乘,效率较低 //因为n!=((n-1)!)*n,即4!=3!*4 //方法二: int n, i = 0; int sum = 0; int ret = 1; scanf("%d", &n); for (i = 1; i <= n; i++) { ret *= i; sum += ret; } printf("%d", sum); return 0; }
3.在一个有序数组中查找具体的某个数字n.
普通的方法:
#includeint main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int k = 7; int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); for (i = 0; i < sz; i++) { if (k == arr[i]) { printf("找到了,他的下标为%d", i); break; } } if (i == sz) { printf("找不到n"); } return 0; }
分析:显然逐一查找效率很低,当从1-10000中找到9999则要花费很长时间.面对有序数组(可由冒泡排序得到),我们可以用二分查找法(折半查找法)
二分查找法:
//二分查找法/折半查找法 #includeint main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int k = 7; int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); int left = 0;//最左边元素下标 int right = sz - 1;//最右边元素下标 while (left<=right) { int mid = (left + right) / 2;//中间元素下标 if (k > arr[mid])//比较中间元素和目标元素 { left = mid - 1; } else if (k < arr[mid]) { right = mid - 1; } else { printf("找到了,他的下标为%dn", mid); break;//找到目标元素后跳出循环 } } if (left > right) { printf("找不到n"); } return 0; }
4.编写代码,演示多个字符从两端移动,向中间汇聚
#include#include #include #include int main() { char arr1[] = "i am a handsome boy"; char arr2[] = "###################"; int left = 0; int right = strlen(arr1) - 1;//stelen计算字符不会把' '算进去 //int right = ( sizeof(arr1) / sizeof(arr[0]) )- 1;这是错误写法.arr1最后一个字符是' ' //int right = ( sizeof(arr1) / sizeof(arr[0]) )- 2;正确,但不推荐,容易出错 while (left <= right) { arr2[left] = arr1[left]; arr2[right] = arr1[right]; printf("%sn", arr2); left++; right--; Sleep(1000); system("cls"); } printf("%sn", arr2); return 0; }
Sleep()函数:
让屏幕休息一段时间,如Sleep(1000),可让屏幕休息1000ms,即1s.其头文件为Windows.h
system()函数:
执行系统命令,如system(“cls”); 为执行系统命令中的一个函数,其作用为清空屏幕,其头文件为stdlib.h
5.编写代码实现,模拟用户登录情景,并且只能登录三次。
#includeint main() { char arr[] = "123456"; char password[21] = ""; int i = 0; printf("请输入密码:n"); for (i = 0; i < 3; i++) { scanf("%s", password); if (0 == strcmp(arr, password)) { break; } else { printf("密码错误,请重新输入:n"); } } if (3 == i) { printf("输入次数用尽"); } else { printf("登陆成功"); } return 0; }
注意:
if (password == arr)
这是错误写法.passwor和arr都是数组的数组名,表示数组的地址,这里比较的是两个数组的地址,显然是不相同的。
比较两个字符串应该使用strcmp()函数:
语法结构:
C语言提供goto和标记跳转的标号,来改变程序流向,转到执行语句标号所标识的语句.
goto语句用途:终止程序在某些深度嵌套的结构的处理过
程,例如一次跳出两层或多层循环.而break只能从某层循环跳到上一层循环.但goto不可跨函数跳转.
使用例子:
int main()
{
again:
printf("hellon");
goto again; //是代码跳到again的位置,此时程序会进入死循环
return 0;
}
下面是有趣的使用goto的关机程序:
提示:
程序运行开始倒计时,若在1min内,输入"我是猪",则取消关机;
否则,嘿嘿嘿~
shutdown -s -60 系统在60秒后关机
代码为:system(“shutdown -s -t 60”);
shutdown -a 取消关机
代码为:system(“shutdown -a”);
两者得包含在头文件stdlib.h里
#include#include #include int main() { char input[20] = { 0 }; system("shutdown -s -t 60"); again: printf("关机程序已开始,若输入:我是猪,将取消关机,请输入:n"); scanf("%s", input); if (0 == strcmp(input, "我是猪")) { system("shutdown -a"); printf("成功取消关机"); } else { printf("输入有误,请重新输入"); goto again; } return 0; }
以Release的形式生成的.exe程序可以发给别人,也可以将该程序放进计算机自动启动的程序中。



