控制流
语句与程序块
在表达式之后加上一个分号(;),它们就变成了语句。例如:
x = 0;
x++;
printf(...);
在C语言中,分号是语句的结束符。用一对花括号“{”于“}”把一组声明和语句括在一起就构成了一个复合语句(也叫做程序块),复合语句在语法上等价于单条语句。右花括号用于结束程序块,其后不需要分号。
if-else语句
if-else语句用于条件判定,其语法示例如下:
if(表达式)
语句1
else
语句2
其中else部分是可选的。该语句执行时,先计算表达式的值,如果其值为真,则执行语句1;如果其值为假,则不执行语句1,如果其有else,则执行语句2。
else-if语句
else-if语句是编写多路判定最常用的方法。其中的各表达式将被依次求值,一旦某个表达式结果为真,则执行与之相关的语句,并且终止整个语句序列的执行。其结构如下:
if(表达式)
语句
else if(表达式)
语句
else if(表达式)
语句
else
语句
例子:二分法查找
/* 二分查找,在v找x,n是数组长度 */
int binsearch(int x,int v[],int n){
int low = 0;
int high = n-1;
int mid;
while(low <= high){
mid = low + (high - low)/2;/* 防止溢出 */
if(v[mid] < x){
low = mid + 1;
}else if(v[mid] > x){
high = mid -1;
}else{
return mid;
}
}
return -1;
}
switch语句
switch语句是一种多路判定语句,它测试表达式是否与一些常量整数值中的某一个值匹配,并执行相应的分支动作。
switch(表达式){
case 常量表达式:语句序列
case 常量表达式:语句序列
default:语句序列
}
每一个分支都由一个或多个整数值常量或常量表达式标记。如果某个分支与表达式的值匹配,则从该分支开始执行。各个分支表达式必须互不相同。如果没有哪一分支能匹配表达式,则执行标记为default的分支。default分支是可选的。如果没有default分支也没有其他分支与表达式的值匹配,则该switch语句不执行任何动作。
例子:
/* 把字符串t复制到s,将转义字符打出来 */
void escape(char s[],char t[]){
int i;
int j=0;
for(i=0;t[i]!='\0';i++){
switch(t[i]){
case '\n':
s[j++] = '\\';
s[j++] = 'n';
break;
case '\t':
s[j++] = '\\';
s[j++] = 't';
break;
default:
s[j++] = t[i];
}
}
s[j] = '\0';
}
break语句将导致程序的执行立即从switch中退出。在switch语句中,case的作用只是一个标号,因此,某个分支中的代码执行完毕后,程序将进入下一个分支继续执行,除非在程序中显示地跳转。
while循环与for循环
在while循环语句
while(表达式)
语句
中首先求表达式的值。如果其值为真非0,则执行语句,并再次求该表达式的值。这一循环过程一直进行下去,直到该表达式的值为假(0)为止,随后继续执行语句后面的部分。
for循环语句:
for(表达式1;表达式2;表达式3)
语句
它等价于下列while语句:
表达式1;
while(表达式2){
语句
表达式3
}
但当while或for循环语句中包含continue语句时,上述二者就不一定等价了。
从语法角度看,for循环语句的3个组成部分都是表达式。最常见的情况是,表达式1与表达式3是赋值表达式或函数调用,表达式2是关系表达式。这三个组成部分的任何部分都可以省略,但分号必须保留。如果省略测试条件,即表达式2,则认为其值永远是真值。如果语句中需要执行简单的初始化和变量递增,使用for语句更合适一些,它将循环控制语句集中放在循环的开头,结构更紧凑,更清晰。
例子:
/* 将字符串逆序 */
void reverse(char s[]){
int i,j;
char temp;
for(i=0,j=strlen(s)-1;i<j;i++,j--){
temp = s[i];
s[i] = s[j];
s[j] = temp;
}
}
逗号运算符“,”,也是C语言优先级最低的运算符,在for语句中经常会用到它。被逗号分隔的一对表达式将按照从左往右的顺序进行求值,各表达式右边的操作数的类型和值即为其结果的类型和值。某些情况下的逗号并不是逗号运算符,不如分隔函数参数的逗号,分隔声明中变量的逗号等,这些逗号并不保证各表达式从左至右的顺序求值。
do-while循环的语法形式如下:
do
语句
while(表达式);
在这一结构中,先执行循环体中的语句部分,然后再求表达式的值。如果表达式的值为真,则再次执行语句。依次类推。当表达式变为假,则循环终止。
例子:
/* 将n转化为对应的字符串,并存储在s中 */
void itoa(int n,char s[]){
int i = 0,sign = 0;
unsigned int m;
if(n<0){
sign = 1;
m = (unsigned int)(-n);
}else{
m = (unsigned int)n;
}
do{
s[i++] = m%10 + '0';
}while((m/=10)>0);
if(sign){
s[i++] = '-';
}
s[i] = '\0';
reverse(s);
}
break语句与continue语句
break语句可用于从for,while与do-while等循环中提前退出,就如同从switch语句中提前退出一样。continue语句用于使for,while或do-while语句开始下一次循环的执行。在while与do-while语句中,continue语句的执行意味着立即执行测试部分;在for循环中,这意味着控制转移到递增循环变量部分。
例子:
/* 删除字符串尾部的空格、制表符和换行符 */
void trim(char s[]){
int n;
for(n=strlen(s)-1;n>=0;n--){
if(s[n]!=' '&&s[n]!='\n'&&s[n] != '\t'){
break;
}
}
s[n+1] = '\0';
}
goto语句与标号
goto语句用于跳转到标号标记的跳转位置。标号的命名通变量命名的形式相同,标号后面要紧跟一个冒号。标号可以位于对应的goto语句所在函数的任何语句的前面。标号的作用域是整个函数。从理论上讲,goto语句是没有必要的,实践中不适用goto语句也可以容易地写出代码。但是,在某些情况下goto语句还是用得着的。最常见的用法是终止程序在某些深度嵌套的结构中的处理过程,例如一次跳出两层或多层循环。例如:
for( ... ){
for( ... ){
...
if(disaster)
goto error;
}
}
error:
处理错误情况
建议尽可能少地使用goto语句。