if-else语句

语法

1
2
3
4
if (表达式)
语句1
else
语句2

else-if语句

语法

1
2
3
4
5
6
7
8
9
10
if (表达式)
语句
else if (表达式)
语句
else if (表达式)
语句
else if (表达式)
语句
else
语句

switch语句

语法

1
2
3
4
5
switch (表达式) {
case 常量表达式 : 语句序列
case 常量表达式 : 语句序列
default 常量表达式 : 语句序列
}

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>


main() /* count digits, white space, others */
{
int c, i, nwhite, nother, ndigit[10];

nwhite = nother = 0;
for (i = 0; i < 10; i++)
ndigit[i] = 0;
while ((c = getchar()) != EOF) {
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
ndigit[c-'0']++;
break;
case ' ':
case '\n':
case '\t':
nwhite++;
break;
default:
nother++;
break;
}
}
printf("digits =");
for (i = 0; i < 10; i++)
printf(" %d", ndigit[i]);
printf(", white space = %d, other = %d\n",
nwhite, nother);
return 0;
}

说明

break语句将导致程序的执行立即从switch语句中退出。在switch语句中,case的作用只是一个标号,因此,某个分支中的代码执行完后,除非使用breakreturn显式跳转,否则程序将进入下一个分支继续执行。 作为一种良好的程序设计风格,在switch语句最后一个分支(即default分支)的后面也加上一个break语句。这样做在逻辑上没有必要,但当我们需要向switch语句后添加其他分支时,这种防范措施会降低错误的可能性。

while循环与for循环

示例

一个更通用的atoi例子,它可以处理可选的前导空白符以及一个可选的加+或减-号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <ctype.h>
/* atoi: convert s to integer; version 2 */
int atoi(char s[])
{
int i, n, sign;
for (i = 0; isspace(s[i]); i++)
;
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-') /* skip white space */
i++;
for (n = 0; isdigit(s[i]); i++) /* skip sign */
n = 10 * n + (s[i] - '0');
return sign * n;
}

逗号运算符,是C语言优先级最低的运算符,在for语句中经常用到它。被逗号符分隔的一对表达式将按照从左到右的顺序进行求值。例如:reverse(s)函数,该函数用于倒置字符串s中各个字符的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <string.h>

/* reverse: reverse string s in place */
void reverse(char s[])
{
int c, i, j;

for (i = 0, j = strlen(s)-1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}

某些情况下的逗号并不是逗号运算符,比如分隔函数参数的逗号,分隔声明中变量的逗号等,这些逗号并不保证各表达式按从左到右的顺序求值。

应该慎用逗号运算符。逗号运算符最适用于关系紧密的结构中,比如上面的reverse函数内的for语句,对于需要在单个表达式中进行多步计算的宏来说也很适合。逗号表达式还适用于reverse函数中元素的交换,这样,元素的交换过程便可以看成是一个单步操作。

1
2
for (i = 0, j = strlen(s)-1; i < j; i++, j--)
c = s[i], s[i] = s[j], s[j] = c;

C++风格的新规则

C99规定了一种新的for循环语法,在控制表达式1的位置可以有变量定义。例如上例的循环变量i可以只在for循环中定义:

1
2
3
4
5
6
7
int factorial(int n)
{
int result = 1;
for(int i = 1; i <= n; i++)
result = result * i;
return result;
}

如果这样定义,那么变量i只是for循环中的局部变量而不是整个函数的局部变量。在循环结束后就不能再使用i这个变量了。这个程序用gcc编译要加上选项-std=c99。这种语法也是从C++借鉴的,考虑到兼容性不建议使用这种写法

do-while循环

示例:itoa函数(atoi函数的逆函数,把数字转换为字符串)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* itoa: convert n to characters in s */
void itoa(int n, char s[])
{
int i, sign;

if ((sign = n) < 0) /* record sign */
n = -n;
/* make n positive */
i = 0;
do { /* generate digits in reverse order */
s[i++] = n % 10 + '0'; /* get next digit */
} while ((n /= 10) > 0); /* delete it */
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}

break语句与continue语句

break语句

break语句可以用于从for、while与do-while等循环中提前退出,就如同从switch语句中提前退出一样。break语句能使程序从switch语句或最内层循环中立即跳出。

下面的函数trim用于删除字符串尾部的空格符、制表符与换行符。当发现最右边的字符为非空格符、非制表符、非换行符时,就使用break语句从循环中退出。

1
2
3
4
5
6
7
8
9
10
/* trim: remove trailing blanks, tabs, newlines */
int trim(char s[])
{
int n;
for (n = strlen(s)-1; n >= 0; n--)
if (s[n] != ' ' && s[n] != '\t' && s[n] != '\n')
break;
s[n+1] = '\0';
return n;
}

continue语句

continue语句用于使forwhiledo-while语句开始下一次循环的执行。在whiledo-while语句中,continue语句的执行意味着立即执行测试部分;在for循环中,则意味着使控制转移到递增循环变量部分。continue语句只用于循环语句,不用于switch语句。某个循环包含的switch语句中的continue语句,将导致进入下一次循环。

goto语句与标号

goto语句

C语言提供了可随意滥用的goto语句以及标记跳转位置的标号。从理论上讲,goto语句是没有必要的,实践中不使用goto语句也可以很容易的写出代码。

但是,在某些场合下goto语句还是用得着的。最常见的做法是终止程序在某些深度嵌套的结构中的处理过程,例如一次跳出两层或多层循环。这种情况下使用break语句是不能达到目的的,它只能从最内层循环退出到上一级的循环。下面是使用goto的一个例子:

1
2
3
4
5
6
7
8
9
for ( ... )
for ( ... ) {
...
if (disaster)
goto error;
}
...
error:
/* clean up the mess */

在该例子中,如果错误处理代码很重要,并且错误可能出现在多个地方,使用goto语句将会比较方便。

标号

标号的命名同变量命名的形式相同,标号的后面要紧跟一个冒号。标号可以位于对应的goto语句所在函数的任何语句的前面。标号的作用域是整个函数。

我们来看另外一个例子。考虑判定两个数组a与b中是否具有相同元素的问题。一种可能的解决方法是:

1
2
3
4
5
6
7
8
9
for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
if (a[i] == b[j])
goto found;
/* didn't find any common element */
...
found:
/* got one: a[i] == b[j] */
...

所有使用了goto语句的程序代码都能改写成不带goto语句的程序,但可能会增加一些额外的重复测试或变量。大多数情况下,使用goto语句的程序段比不使用goto语句的程序段要难以理解和维护,少数情况除外。因此,建议尽可能少的使用goto语句。

Comments