目录
1. 分支语句
分支语句又称选择语句、条件判断语句。
1.1 if
1.1.1 语法结构
if (表达式)
语句
if (表达式)
语句1
else
语句2
if (表达式1)
语句1
else if (表达式2)
语句2
...
else if (表达式n)
语句n
else
语句n+1
1.1.2 演示代码
if (age < 18)
{
printf("未成年\n");
printf("不能饮酒\n");
} // 控制多条语句要用{}括起来
if (age < 18)
{
printf("未成年\n");
printf("不能饮酒\n");
}
else
printf("成年\n");
if (age <= 6)
printf("童年\n");
else if (age >= 7 && age <= 17)
printf("少年\n");
else if (age >= 18 && age <= 40)
printf("青年\n");
else if (age >= 41 && age <= 65)
printf("中年\n");
else
printf("老年\n");
1.1.3 悬空else问题
没有{}的约束下else和离它最近的if匹配。
例题1
#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 (b == 2)匹配,上述代码规范写法如下:
#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
#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 (a == 1)匹配。代码运行结果为:haha
例题3
#include <stdio.h>
int main()
{
int a = 1, b = 2, c = 3, d = 0;
if (a == 1 && b++ == 2)
if (b != 2 || c-- != 3)
printf("%d,%d,%d\n", a, b, c);
else
printf("%d,%d,%d\n", a, b, c);
else
printf("%d,%d,%d\n", a, b, c);
return 0;
}
第一个else和if (b != 2 || c-- != 3)匹配,第二个else只能和if (a == 1 && b++ == 2)匹配。上述代码规范写法如下:
#include <stdio.h>
int main()
{
int a = 1, b = 2, c = 3, d = 0;
if (a == 1 && b++ == 2)
{
if (b != 2 || c-- != 3)
printf("%d,%d,%d\n", a, b, c);
else
printf("%d,%d,%d\n", a, b, c);
}
else
printf("%d,%d,%d\n", a, b, c);
return 0;
}
代码如何执行?
- 初始值:a = 1, b = 2, c = 3, d = 0
- 表达式a == 1 && b++ == 2为真,进入{}
- 执行b++操作:a = 1, b = 3, c = 3, d = 0
- 表达式b != 2 || c-- != 3中的b != 2为真,可以确定整个表达式为真,不执行c-- != 3(短路原则)
- 打印a,b,c:1,3,3
1.2 switch
1.2.1 语法结构
switch (整型常量表达式)
{
case 整型常量表达式1:
语句1
break;
case 整型常量表达式2:
语句2
break;
...
case 整型常量表达式n:
语句n
break;
default:
语句n + 1
break;
}
switch (整型常量表达式)
{
case 整型常量表达式1:
....
case 整型常量表达式a:
语句1
break;
case 整型常量表达式a+1:
....
case 整型常量表达式b:
语句2
break;
....
case 整型常量表达式c:
....
case 整型常量表达式d:
语句n
break;
default:
语句n+1
break;
}
1.2.2 演示代码
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;
}
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("工作日\n");
break;
case 6:
case 7:
printf("休息日\n");
break;
default:
printf("输入有误,请重新输入\n");
break;
}
1.2.3 case穿透现象
在switch语句中,如果case控制的语句体后面不写break,将出现穿透现象。在不判断下一个case值的情况下,向下运行,直到遇到break,或者整体switch语句结束。
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
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");
break;
}
return 0;
}
2. 循环语句
循环语句又称迭代语句。
2.1 while
2.1.1 语法结构
while (表达式)
循环体
2.1.2 演示代码
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
printf("%d ", i);
i++;
}
return 0;
}
// 1 2 3 4 5 6 7 8 9 10
2.2 for
2.2.1 语法结构
for (初始化表达式; 条件判断表达式; 调整表达式)
循环体
2.2.2 演示代码
#include <stdio.h>
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
printf("%d ", i);
}
return 0;
}
// 1 2 3 4 5 6 7 8 9 10
2.2.3 表达式的省略
三个表达式都是可以省略的:
- 省略初始化表达式表示不对循环控制变量赋初值
- 省略条件判断表达式表示判断恒成立
- 省略调整表达式表示不对循环控制变量做调整
全部省略即for(;;)表示无限循环,相当于while(1)。
2.2.4 C99中的for语句
在C99中,for语句的第一个表达式可以替换为一个声明,这一特性使得程序员可以声明一个用于循环的变量:
// 声明一个变量
for (int i = 0; i < 10; i++)
{
//...
}
// 声明多个变量,只要它们的类型相同
for (int i = 0, j = 0; i < 10; i++)
{
//...
}
变量i不需要在该语句前进行声明。事实上,如果变量i在之前已经进行了声明,这个语句将创建一个新的i且该值仅用于循环内。
for语句声明的变量不可以在循环外访问(在循环外不可见)。
2.2.5 C++11 范围for
在这里提一下C++的范围for(不是C语言的语法,是C++的语法)。
C++11新标准引入了一种更简单的for语句,这种语句可以遍历容器或其他序列的所有元素。范围for语句(range for statement)的语法形式是:
for (declaration : expression)
statement
expression表示的必须是一个序列,比如用花括号括起来的初始值列表、数组或者vector或string等类型的对象,这些类型的共同特点是拥有能返回迭代器的begin和end成员。
declaration定义一个变量,序列中的每个元素都得能转换成该变量的类型。确保类型相容最简单的办法是使用auto类型说明符,这个关键字可以令编译器帮助我们指定合适的类型。如果需要对序列中的元素执行写操作,循环变量必须声明成引用类型。
每次迭代都会重新定义循环控制变量,并将其初始化成序列中的下一个值,之后才会执行statement。像往常一样,statement可以是一条单独的语句也可以是一个块。所有元素都处理完毕后循环终止。
之前我们已经接触过几个这样的循环。接下来的例子将把vector对象中的每个元素都翻倍,它涵盖了范围for语句的几乎所有语法特征:
vector<int> v = { 0,1,2,3,4,5,6,7,8,9 };
// 范围变量必须是引用类型,这样才能对元素执行写操作
// C++11中,auto仅有自动推断变量类型的功能
for (auto& r : v)
r *= 2; // 将v中每个元素的值翻倍
for语句头声明了循环控制变量r,并把它和v关联在一起,我们使用关键字auto令编译器为r指定正确的类型。由于准备修改v的元素的值,因此将r声明成引用类型。此时,在循环体内给r赋值,即改变了r所绑定的元素的值。
范围for语句的定义来源于与之等价的传统for语句:
for (auto beg = v.begin(), end = v.end(); beg != end; ++beg)
{
auto& r = *beg; // r必须是引用类型,这样才能对元素执行写操作
r *= 2; // 将v中每个元素的值翻倍
}
学习了范围for语句的原理之后,我们也就不难理解为什么强调不能通过范围for语句增加vector对象(或者其他容器)的元素了。在范围for语句中,预存了end()的值。一旦在序列中添加(删除)元素,end函数的值就可能变得无效了。
2.3 do while
2.3.1 语法结构
do
{
循环体
} while (表达式);
2.3.2 演示代码
#include <stdio.h>
int main()
{
int i = 10;
do
{
printf("%d ", i);
} while (i < 10);
return 0;
}
// 10
// 先执行一次循环体,再判断表达式
// 即do while循环结构至少执行一次循环体
3. 跳转语句
跳转语句又称转向语句。
3.1 break
break用于跳出当前的那一层循环或switch语句。
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
if (5 == i)
break;
printf("%d ", i);
i++;
}
return 0;
}
// 1 2 3 4
#include <stdio.h>
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (5 == i)
break;
printf("%d ", i);
}
return 0;
}
// 1 2 3 4
#include <stdio.h>
int main()
{
int i = 1;
do
{
if (5 == i)
break;
printf("%d ", i);
i++;
}
while (i < 10);
return 0;
}
// 1 2 3 4
#include <stdio.h>
int main()
{
int day = 6;
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;
}
// 星期六
3.2 continue
continue用于终止本次循环,只要遇到continue,后面的代码就不会执行,直接进入下一次循环的入口。
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
if (5 == i)
continue;
printf("%d ", i);
i++;
}
return 0;
}
// 1 2 3 4 死循环
#include <stdio.h>
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (5 == i)
continue;
printf("%d ", i);
}
return 0;
}
// 1 2 3 4 6 7 8 9 10
#include <stdio.h>
int main()
{
int i = 1;
do
{
if (5 == i)
continue;
printf("%d ", i);
i++;
}
while (i < 10);
return 0;
}
// 1 2 3 4 死循环
3.3 goto
goto语句又称无条件转移语句。
[标号语句] 标识符: 语句
[goto语句] goto 标识符;
标号是放置在语句开始处的标识符,一条语句可以有多个标号。
goto语句可以跳转到函数中任何有标号的语句处。
goto语句可以用于跳出多层循环,但是不建议使用,以免造成程序流程的混乱。
做一个关机程序:60s后关机,如果输入:我是猪,则取消关机。
使用goto语句:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char input[20] = { 0 };
system("shutdown -s -t 60"); // 执行系统命令:60s后关机
again:
printf("请注意,你的电脑将在60秒后关机,如果输入:我是猪,则取消关机\n");
scanf("%s", input);
if (strcmp(input, "我是猪") == 0)
{
system("shutdown -a"); // 执行系统命令:取消关机
}
else
{
goto again;
}
return 0;
}
使用while循环:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
}
3.4 return
return 表达式;
有返回值的C函数要有return语句。如果遗漏main函数中的return语句,程序在运行至最外面的右花括号( } )时会返回0。因此,可以省略main函数末尾的return语句(强烈建议不要省略)。但是,不要在其他有返回值的函数中漏掉它。
在main函数中执行return语句是终止程序的一种方法,另一种方法是调用exit函数,此函数属于<stdlib.h>头。传递给exit函数的实际参数和main函数的返回值具有相同的含义:两者都说明程序终止时的状态。
为了表示正常终止,传递0:
exit(0);
因为0有点模糊,所以C语言允许用EXIT_SUCCESS来代替(效果是相同的):
exit(EXIT_SUCCESS);
传递EXIT_FAILURE表示异常终止:
exit(EXIT_FAILURE);
EXIT_SUCCESS和EXIT_FAILURE都是定义在<stdlib.h>中的宏。EXIT_SUCCESS和EXIT_FAILURE的值都是由实现定义的,通常分别是0和1。
作为终止程序的方法,return语句和exit函数关系紧密。事实上,main函数中的语句
return 表达式;
// 等价于
exit(表达式);
return语句和exit函数之间的差异是:不管哪个函数调用exit函数都会导致程序终止,return语句仅当由main函数调用时才会导致程序终止。