C语⾔是结构化的程序设计语⾔,这⾥的结构指的是顺序结构、选择结构、循环结构。
这里讲的是选择结构和循环结构的使用。
在C语言中,我们可以使⽤ if 、 switch 实现分⽀结构,使⽤ for 、 while 、 do while 实现循环结构。
1.if介绍
1.1 if语句使用方法
if语句的语法形式如下:
if(表达式)
语句
我们前面说过,在C语言中,0为假,非0为真,
当if语句中表达式为非零(真),则语句执行,表达式结果为0(假),则语句不执行。
**例子:**输入一个整数,判断是否为奇数。
#include <stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
if (a % 2 == 1)
{
printf("%d是奇数\n", a);
}
return 0;
}
我们输入一个数 5 ,它的输出结果为:
if语句的流程图如下:
从上面的if语句中我们可以画出奇数判断的流程图:
1.2 else语句
如果⼀个数不是奇数,那就是偶数了,如果任意⼀个整数,我们要清楚的判断是奇数还是偶数怎么表示呢?
这⾥就需要 if…else… 语句了,语法形式如下:
if(表达式)
语句1
else
语句2
**例子:**输入一个整数,判断是否为奇数,如果是奇数打印奇数 ,否则打印偶数。
#include <stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
if (a % 2 == 1)
{
printf("%d为奇数", a);
}
else
{
printf("%d是偶数", a);
}
return 0;
}
运行结果如下:
下面我们做一个练习:输⼊⼀个年龄,>= 18岁就输出:成年,否则就输出:未成年
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if (age >= 18)
{
printf("成年");
}
else
{
printf("未成年");
}
return 0;
}
1.3分支中包含多条语句
上面的代码中,我们在if和else语句都加上了大括号{},这么做的原因除了为了容易阅读代码以外,还有另一个原因就是if和else语句默认都只控制一条语句,比如:
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if (age >= 18)
printf("成年了\n");
printf("可以谈恋爱了\n");
return 0;
}
上面的代码,你会发现输⼊的值不管是>=18 还是⼩于18,“可以谈恋爱了” 都会打印在屏幕上。
这是因为 if 语句只能控制⼀条语句,就是 printf(“成年了\n”); ,if语句为真,则打印成年了,if语句为假,则不打印,而 printf(“可以谈恋爱了\n”); 是独立存在的,它是不受 if语句控制的,不管if语句的条件的真假,都会被执行。那如果我们要if语句同时控制2条以上的语句,怎么办呢?那就要使⽤ {} 将代码括起来,else 后也可以跟上⼤括号。
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if (age >= 18)
{
printf("成年了\n");
printf("可以谈恋爱了\n");
}
return 0;
}
运行结果如下:
else使用示例:
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if (age >= 18)
{
printf("成年了\n");
printf("可以谈恋爱了\n");
}
else
{
printf("未成年\n");
printf("不可以谈恋爱了\n");
}
return 0;
}
1.4 嵌套if
在if…else语句中,else可以与另一个if语句连用,构成多重判断。
比如:要求输⼊⼀个整数,判断输⼊的整数是0,还是正数或者负数。请看如下代码:
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
if (num == 0)
{
printf("输入的数是0");
}
else if (num > 0)
{
printf("输入的数是正数");
}
else
{
printf("输入的数是负数");
}
return 0;
}
上面代码中,第一个else后面跟随的if语句就是嵌套在了else语句中的,构成嵌套的if语句。
再比如:
输⼊⼀个整数,如果是正数,再判断是奇数还是偶数,并输出;如果不是正数则输出:负数。
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
if (num == 0)
{
printf("输入的数是0");
}
else if (num > 0)
{
if (num % 2 == 1)
{
printf("输入的数是奇数");
}
else
{
printf("输入的数是偶数");
}
}
else
{
printf("输入的数是负数");
}
return 0;
}
上面代码中的奇偶数判断就是嵌套在正数判断的if语句中的,构成嵌套的if语句。
以上就是嵌套if语句的语法,有了嵌套if语句,就可以完成更多复杂逻辑的判断。
下面我们做一个练习:
输⼊⼀个⼈的年龄
1 如果年龄<18岁,打印"少年"
2 如果年龄在18岁⾄44岁打印"⻘年"
3 如果年龄在45岁⾄59岁打印"中⽼年"
4 如果年龄在60岁⾄89岁打印"⽼年"
5 如果90岁以上打印"⽼寿星"
参考代码如下;
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
if (num < 18)
{
printf("少年\n");
}
else if(num < 45)
{
printf("青年\n");
}
else if (num < 60)
{
printf("中老年\n");
}
else if (num < 90)
{
printf("中老年\n");
}
else {
printf("老寿星\n");
}
return 0;
}
1.5悬空else问题
下面我们看一个代码:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a == 1)
if (b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
大家可以思考一下上面的代码运行的结果是什么?
对于初学者来说,上来判断 a 是 0,不等于 1 ,那就执⾏ else ⼦句,打印 haha。
但是结果却是什么都不打印。
为什么会这样呢?
这就是悬空else的问题,在C语言中,如果有多个if和else,可以记住这样⼀条规则,else总是跟最接近的if匹配。
上面的代码因为排版的问题,让我们误认为else是和第一个if相对应的,所以在判断a == 1这句代码时,条件为假,而else语句此时和第一个if对齐的,让我们自然而然的就认为会输出hah。
如果改成下面这样我们就容易理解为何会这样了:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a == 1)
if (b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
或者如果我们希望else确实和第⼀个if匹配,可以这样修改代码:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a == 1)
{
if (b == 2)
printf("hehe\n");
}
else
{
printf("haha\n");
}
return 0;
}
只要带上适当的大括号,代码的逻辑就会更加的清晰,所以⼤家以后在写代码的时候要注意括号的使用,让代码的可读性更高。
2.关系操作符
C 语⾔用于比较的表达式,称为 “关系表达式”(relational expression),里面使⽤的运算符就称
为“关系运算符”(relational operator),主要有下⾯6个。
- >:大于运算符
- <:小于运算符
- >=:大于等于运算符
- <=:小于等于运算符
- ==:相等运算符,这里要注意,它和 =是两个不一样的运算符,不能混淆。
- !=:不等于运算符
下面是一些例子。
1 a == b;
2 a != b;
3 a < b;
4 a > b;
5 a <= b;
6 a >= b;
关系表达式通常返回0或1,表达真假。
C 语⾔中, 0 表示假,所有非零值表示真。比如, 20 > 12 返回 1 , 12 > 20 返回 0 。
而这些关系运算符经常用在判断语句if或循环语句while中。
在关系操作符介绍中说过,==和=是不一样的,它是两个意义的符号,如果有时候不小心写错了,虽然是能运行的,但是结果却是想象不到的。例如:
if(x = 3)
上面的代码实际情况是 把数值3赋予给变量x,虽然我们想表达的意思是判断x的值是否和3相等,但是由于种种原因导致我们少输入了一个=。因此这行代码它的返回值为3,我们说过非零为真,所以if这里会始终为真,转而运行下面代码。
为了防止出现这种错误,我们可以将变量卸载等号的右边。
if(3 == x)
像上面这种写法,如果我们把==误写成=,编译器就会报错。
还有一个初学者需要避免的错误是:**多个关系运算符不能连用。**如下所示:
i < j <k
上面的示例中,连续使用两个小于运算符,这个表达式在编译器中是合法的,不会报错,但是它运行后的结果却不是我们想要的变量j大于i小于k的结果.
它实际的运行结果是按照下面的方式运行的:
如果想要判断变量j的值是否在i和j之间,应该使用下面的写法。
i < j && j < k
3.条件操作符
条件操作符也叫做三目操作符,需要接收三个操作数的操作符,也是唯一一个三木操作符。形式如下。
exp1 ? exp2 : exp3
上面的代码中exp的意思是表达式。
条件操作符的计算逻辑是:如果 exp1为真, exp2 计算,计算的结果是整个表达式的结果;如果exp1 为假, exp3 计算,计算的结果是整个表达式的结果。可以根据流程图理解为:
下面做个练习:使⽤条件操作符表示下⾯代码的逻辑
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
if (a > 5)
b = 3;
else
b = -3;
printf("%d\n", b);
return 0;
}
改造后
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
b = a > 5 ? 3 : -3;
printf("%d\n", b);
return 0;
}
第二个练习:使用条件表达式找出两个数值的最大值
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d%d", &a, &b);
int max = a > b ? a : b;
printf("最大值是%d\n",max);
return 0;
}
我们看下运行情况:
4.逻辑 操作符: && , || , !
逻辑运算符主要是用来逻辑判断的功能,用于比较复杂的表达式,常用的逻辑操作符有以下三个:
- !:逻辑取反
- && :逻辑与
- || :逻辑或
4.1逻辑取反运算符 !
逻辑取反运算符,就是改变单个表达式的真假,如下如所示,当a为真时,那么!a则为假,相反的,当a为假时,那么!a则为真。
注:C语言中,非零表示为真,0表示为假。
举例:有一个变量flag,如果flag为假,就输出do something。
#include <stdio.h>
#include <stdbool.h>
int main()
{
bool flag = false;
if (!flag)
{
printf("do something\n");
}
return 0;
}
上面代码中,我们赋予变量flag为布尔型,值为假。根据if语句的运行规则,当里面的表达式为真时,执行其中的语句,而此时flag的值为假,那么!flag的值就为真,运行结果如下所示:
4.2逻辑与运算符 &&
逻辑与的表现形式如下表格所示:
注:C语言中,非零表示为真,0表示为假。
&& 的意思是并且的意思,&& 是一个双目操作符,使用的方式为a&&b 。
&& 两边的表达式都是真的时候,整个表达式的值才为真,只要a或者b中有一个是假,那么整个表达式的值就是假。
举例说明:3月到5月是春天,其代码如下,
#include <stdio.h>
int main()
{
int month = 0;
scanf("%d", &month);
if (month > 3 && month < 5)
{
printf("春天\n");
}
return 0;
}
运行结果如下:
这⾥表达的意思就是month既要⼤于等于3,⼜要⼩于等于5,必须同时满⾜。
4.3 逻辑或运算符 ||
逻辑或的表现形式如下表格所示:
注:C语言中,非零表示为真,0表示为假。
|| 就是或运算符,也就是或者的意思, || 也是一个双目操作符,使用的方式是 a || b , || 两边的表达式只要有一个是真的,整个表达式就是真,两边的表达式都为假的时候,才为假。
⽐如:我们说⼀年中⽉份是12⽉或者1⽉或者2⽉是冬天,那么我们怎么使⽤代码体现呢?
#include <stdio.h>
int main()
{
int month = 0;
scanf("%d", &month);
if (month ==1 || month == 2 || month == 12)
{
printf("冬天\n");
}
else {
printf("不是冬天\n");
}
return 0;
}
运行结果如下:
4.4逻辑运算符练习
输⼊⼀个年份year,判断year是否是闰年。
闰年判断的规则:
- 能被4整除并且不能被100整除是闰年
- 能被400整除是闰年
代码1:
#include <stdio.h>
int main()
{
int year = 0;
scanf("%d", &year);
if (year % 4 == 0 && year % 100 != 0)
{
printf("是闰年\n");
}
else if (year % 400 == 0)
{
printf("是闰年\n");
}
return 0;
}
代码2:
#include <stdio.h>
int main()
{
int year = 0;
scanf("%d", &year);
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
printf("是闰年\n");
}
return 0;
}
4.5短路
C语⾔逻辑运算符还有⼀个特点,它总是先对左侧的表达式求值,再对右边的表达式求值,这个顺序是
保证的。
如果左边的表达式满⾜逻辑运算符的条件,就不再对右边的表达式求值。这种情况称为“短路”。
如前面的代码:
if(month > 3 && month < 5)
表达式中&& 的左操作数是 month > 3 ,右操作数是 month < 5 ,当左操作数 month > 3 的结果是假的时候,即使不判断 month < 5 ,整个表达式的结果也是假(值为0),不是春季。
所以,对于&&操作符来说,左边操作数的结果为假的时候,右边操作数就不再执⾏。
对于||操作符又是什么情况呢?我们结合前面的阿代码:
if (month ==1 || month == 2 || month == 12)
如果month == 1,则不⽤再判断month是否等于1或者12,整个表达式的结果也是1(是冬季)。
所以, || 操作符的左操作数的结果不为0时,就⽆需执行右操作数。
像这种仅仅根据左操作数的结果就能知道整个表达式的结果,不再对右操作数进行计算的运算称为短路求值。
关于短路详细解读,请参考这里:C语言中的短路求值介绍。
5.switch语句
除了if语句外,C语言中还提供了switch语句来实现分支结构。
switch语句时一种特殊形式的if…else结构,用于判断条件有多个结果的情况,它把多重的elseif语句变成更易用、可读性更好的形式。
使用方法如下:
switch (表达式)
{
case 值1: 语句
case 值2: 语句
........
default: 语句
}
上面代码中,根据表达式不同的值,执⾏相应的 case 分⽀。如果找不到对应的值,就执⾏ default 分⽀。
需要注意的是:
1 switch 后的表达式必须是整型表达式
2 case后的值,必须是整形常量表达式
5.1 if语句和switch语句的对比
练习:输入一个整数,判断是星期几。
如果用if语句完成,它的代码如下:
#include <stdio.h>
int main()
{
int week = 0;
scanf("%d", &week);
if (week == 1)
{
printf("星期一\n");
}else if(week == 2)
{
printf("星期二\n");
}
else if (week == 3)
{
printf("星期三\n");
}
else if (week == 4)
{
printf("星期四\n");
}
else if (week == 5)
{
printf("星期五\n");
}
else if (week == 6)
{
printf("星期六\n");
}
else if (week == 7)
{
printf("星期天\n");
}
return 0;
}
如果使⽤switch语句改写,就可以是这样的:
#include <stdio.h>
int main()
{
int week = 0;
scanf("%d", &week);
switch (week)
{
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;
}
return 0;
}
注意
1.case 和后边的数字之间必须有空格
2. 每⼀个 case 语句中的代码执⾏完成后,需要加上 break ,才能跳出这个switch语句。
5.2 switch语句中的break
前⾯的代码中,如果我们去掉case语句中的break,会出现什么情况呢?
我们修改下:
#include <stdio.h>
int main()
{
int week = 0;
scanf("%d", &week);
switch (week)
{
case 1:
printf("星期一\n");
case 2:
printf("星期二\n");
case 3:
printf("星期三\n");
case 4:
printf("星期四\n");
case 5:
printf("星期五\n");
case 6:
printf("星期六\n");
case 7:
printf("星期天\n");
}
return 0;
}
运行上面代码,测试结果如下:
出问题了,怎么星期六星期天也被打印出来了,为什么呢?
原因是switch语句也是分⽀效果的,只有在switch语句中使⽤ break才能在跳出switch 语句,如果某⼀个 case 语句的后边没有 break 语句,代码会继续往下执⾏,有可能执⾏其他 case语句中的代码,直到遇到 break语句或者 switch 语句结束。就比如上⾯的代码就执⾏了 case 6和 case 7中的语句。
所以在switch语句中 break语句是⾮常重要的,能实现真正的分⽀效果。
当然, break也不是每个 case语句都得有,这就得根据实际情况来看了。
比如下面这题:
- 输⼊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;
}
上⾯的练习中,我们发现应该根据实际的情况,来在代码中觉得是否使⽤ break ,或者在哪⾥使⽤
break,才能正确完成实际的需求。
5.3 switch语句中的default
在第一段判断星期几的代码中,用户可能会输入其他数值,比如输入8,但是8在我们的代码中无法匹配case语句的值,这时候我们要么不做处理,要不就要在switch语句中加入default子句。
switch (表达式)
{
case 值1: 语句
case 值2: 语句
........
default: 语句
}
switch 后边的表达式的结果不是 值1 ,也不是 值2 的时候,就会执⾏default 后面的语句。
我们把打印星期的代码修改如下:
int main()
{
int week = 0;
scanf("%d", &week);
switch (week)
{
case 1:
printf("星期一\n");
case 2:
printf("星期二\n");
case 3:
printf("星期三\n");
case 4:
printf("星期四\n");
case 5:
printf("星期五\n");
case 6:
printf("星期六\n");
case 7:
printf("星期天\n");
default:
printf("输入错误,退出\n");
}
return 0;
}
运行结果:
5.4 switch语句中的case和default的顺序问题
在switch语句中case⼦句和default⼦句有要求顺序吗?default只能放在最后吗?
其实,在switch语句中case语句和default语句是没有顺序要求的,只要你的顺序是满⾜实际需求的就可以。
不过通常情况下都是把default⼦句放在最后处理的,这样更有助于代码的阅读。