第5章 语 句

来源:互联网 发布:启用访客网络什么意思 编辑:程序博客网 时间:2024/06/05 07:33

第5章 语 句

一个C程序的语句控制一个程序执行的流程。在C中,和其它程序设计语言中一样,有几种类型的语句可以用来执行循环、选择其它执行的语句以及转换控制。后面简要概述语句语法,本章以字母排序次序描述这些C语句:

break语句 if语句

复合语句 空语句

continue语句 return语句

do-while语句 switch语句

表达式语句 try-except语句

for语句 try-finally语句

goto和标号语句 while语句


语句概述

C语句由语句符号、表达式和其它语句组成。一个构成另一个语句的组成成分的语句被称为“体”。本章讨论了如下语法给出的每种语句类型。

语法

语句:

标号语句

复合语句

表达式语句

选择语句

迭代语句

跳转语句

try-except语句 /* Microsoft特殊处 */

try_finally语句 /* Microsoft特殊处 */

语句体频繁地是作为“复合语句”,一个复合语句由其它包括关键字的语句组成。复合语句通过花括号({})限定。所有其它C语句以一个分号(;)结尾。一个分号是一个语句结束符。

表达式语句包含一个C表达式,该C表达式包含第4章“表达式和赋值”中介绍的算术和逻辑运算符。null语句是一个空语句。

任何C语句可以以一个标识的标号开头,该标号由一个名称加一个冒号组成。由于只有goto语句识别语句标号,语句标号与goto一起讨论,有关更多信息参见本章后面的“goto和标号语句”。


break语句

break语句用于终止包含它的do、for、switch或while语句的执行。控制传递给该终止语句的后续语句。

语法

跳转语句:

break;

break语句频繁地使用在一个switch语句中以终止一个特殊情况的处理。缺少包含它的迭代语句或switch语句时产生一个错误。

在嵌套的语句中,break语句仅终止直接包含它的do、for、switch或while语句。

你可以使用一个return或goto语句把控制转向该嵌套结构的外面的地方。

如下例子说明了break语句:

for (i=0,i<LENGTH;i++)  /*当执行 break语句时控制返回到这里*/
{
for(j=0,j<WIDTH;j++)
{
if (lins[i][j]==′/0′)
{
lengths[i]=j;
break;
}
}
}

这个例子处理一个存储在lines中的可变长度字符串的数组。break语句

导致在每个字符串中找到结尾的空格字符(′/0′)后退出里层的for循环并把该位置存储在lengths[i]中。

在break导致从里层循环退出时变量j不增大。然后控制返回到外层for循环。i增大且重复这个过程直到i大于或等于LENGTH。


复合语句

一个复合语句(也称为一个“块”)一般作为另一语句例如if语句的语句体出现。第3章“说明和类型”描述了出现在一个复合语句开头的说明的格式和含义。

语法

复合语句:

{说明表opt 语句表opt}

说明表:

说明

说明表 说明

语句表:

语句

语句表 语句

如果有说明,它们必须放在任何语句之前。每个在一个复合语句开头说明的标识符的范围从其说明点开始延伸到该块结束。它在该块中是可见的,除非在一个更里层的块中存在相同名称的标识符的说明。

在一个复合语句中的标识符都假定是auto,除非用register、static或extern进行显式说明,除了函数之外,因为函数只能是extern。你可以在函数说明中省略extern指示符,该函数仍然是extern的。

如果一个变量或函数在一个复合语句中用存储类extern说明,则不分配存储,不能进行初始化。该说明指的是在其它地方定义的一个外部变量或函数。

在一个块中用auto或register关键字说明的变量被重新说明,如果必要,在每次进入该复合语句时进行初始化。这些变量在该复合语句退出时就没有定义了。如果一个变量在一个块内说明并具有static属性,在程序执行开始时初始化该变量并在整个程序中保存它的值。有关static的信息参见第3章“说明和类型”中的“存储类”。

这个例子说明一个复合语句:

if (i>0)
{
line[i]=x;
x++;
i--;
}

在这个例子中,如果i大于0,那么该复合语句中的所有语句依次执行。


continue语句

continue语句把控制传递给包含该语句的do、for或while语句的下一次迭代,绕过do、for或while语句体的任何余下语句。 continue的一般使用是从一个深的嵌套循环中返回到一个循环的开头。

语法

跳转语句:

continue;

确定一个do、for或while语句的下一次迭代如下:

* 在一个do或一个while语句中,通过重新do或while语句中表达式的求值来开始下一次迭代。

* 在一个for语句中的continue语句导致for语句的第一个表达式被求值,然后编译器重新条件表达式的求值,根据其结果,终止或迭代该语句体。有关for语句和它的非终结符的更多信息参见本章后面的“for语句”。

如下是continue语句的例子:

while(i-->0)
{
x=f(i);
if(x==1)
continue;
y+=x*x;
}

在这个例子中,当i大于0时执行该语句体。首先把f(i)赋给x,然后如果x等于1,则执行continue语句,忽略语句体中其余的语句,执行回到该循环的顶部,求值该循环的测试。


do-while语句

do-while语句让你重复一个语句或复合语句直到一个指定的条件变成假。

语法

迭代语句:

do 语句 while(表达式);

在一个do-while语句中的表达式在执行该循环体之后求值,因此该循环体至少执行一次。

该表达式必须是算术或指针类型。执行过程如下:

1. 执行语句体。

2. 接着对表达式求值,如果该表达式为假,该do-while语句终止,控制转向程序中的下一个语句,如果表达式为真(非0),重复该过程,从第1步开始。

当执行语句体中的一个break、goto或return语句时也终止该do-while语句。如下是do-while语句的例子:

do
{
y=f(x);
x--;
}
while(x>0);

在这个do-while语句中,执行y=f(x);和x--;两个语句,不论x的初值如何。然后求值x>0,如果x大于0,则又执行该语句体和重新求值x>0,只要x保持大于0,则重复执行该语句体。当x变成0或负数时终止该do-while语句的执行,该循环体至少执行一次。


表达式语句

当执行一个表达式语句时,根据第4章“表达式和赋值”中给出的规则对该表达式求值。

语法表达式语句:

表达式opt

一个表达式求值的所有副作用都在执行下一个语句之前完成。一个空表达式语句称为一个空语句。有关更多信息参见本章后面的“空语句”。这些例子说明了表达式语句:

x=(y+3); /* 把y+3的值赋给

x*/x++; /*x增1*/

x=y=0 /*把x和y都初始化为0 */

proc(arg1,arg2); /*函数调用返回void */

y=z=(f(x)+3); /*一个函数调用表达式 */

在最后一个函数调用表达式语句中,该表达式的值包括该函数返回的任何值再增大3,然后赋给变量y和z。


for语句

for语句让你重复一个语句或复合语句指定的次数。一个for语句体执行0次或多次直到一个任选的条件变成假。你可以在for语句中使用任选表达式并在for结构的执行中初始化和改变值。

语法

迭代语句:

for (初始表达式opt;条件表达式opt;循环表达式opt) 语句

一个for语句的执行过程如下:

1. 如果有,则求值初始表达式,这指出该循环的初始化。初始表达式在类型上没有限制。

2. 如果有,则求值条件表达式,这个表达式必须是算术或指针类型。它在每次迭代之前求值,有三种可能的结果:

* 如果条件表达式为真(非0),则执行该语句。然后如果有循环表达式,则对其求值。在每次迭代之后都对循环表达式求值。其类型没有限制。副作用将依次执行。然后又开始求值条件表达式的过程。

* 如果省略了条件表达式,条件表达式被认为是真,执行过程和前面段中描述的过程完全一样。一个没有条件表达式的for语句仅在执行

语句体中的break或return语句时终止,或者在执行一个(跳出for语句体)goto语句时终止。

* 如果条件表达式为假(0),for语句的执行终止,并把控制传递给程序中的下一个语句。

一个for语句也在执行语句体中的break、goto或return语句时终止。一个for循环中的continue语句导致求值循环表达式。当执行一个for循环中的break语句时,循环表达式不求值或执行。如下语句:

for(;;);

是一个通常的产生无限循环的方式,它只能用break、goto或return语句退出。

如下例子说明for语句:

for(i=space=tab=0;i<MAX,i++)
{
if (line[i]== ′′)
space++;
if (line[i]== ′/t′)
{
tab++;
line[i]= ′′;
}
}

这个例子对名称为line的字符数组中的空格(′′)和制表键(′/t′)进行

计数,并用一个空格替换每个制表键,首先i、space和tab都初始化为0。然后i和常量MAX进行比较;如果i小于MAX,则执行该语句体。根据line[i]的值,执行或不执行if语句的语句体。然后i增1并又与MAX比较,只要i小于MAX则重复地执行该语句体。


goto和标号语句

goto语句转换控制到一个标号。给定的标号必须在相同的函数中,而且只能出现在相同函数中的一个语句的前面。

语法

语句:

标号语句

跳转语句

跳转语句:

goto 标识符标号语句:

标识符:语句一个语句标号仅对goto语句有意义,在任何其它上下文中,一个标号语句的执行忽略该标号。

一个跳转语句必须保留在相同的函数中,只能出现在相同函数中一个语句之前。跟随一个goto的标识符名称有它自己的名称空间,因此该名称与其它标识符无关。标号不能重新说明。有关更多信息参见第2章“程序结构”中的“名称空间”。

一种好的程序设计风格是在可能出现goto的地方优先使用break、continue和return语句。一个break语句只能从一个循环层退出,而一个goto可以从深的嵌套循环中退出。

如下例子说明了goto语句的作用:

void main()
{
int i,j;
for (i=0,i<10;i++)
{
printf("Outer loop executing. i=%d/n",i);
for(j=0;j<3;j++)
{
printf("Inner loop executing. j=%d/n",j),
if (i==5)
goto stop;
}
}
/* 这个消息不打印 */ printf ("Loop exited. i=%d/n",i); stop:printf("Jumped to stop,i=%d/n",i);
}

在这个例子中,在i等于5时一个goto语句把控制转向括号为stop的语句。


if 语句

if语句控制条件分支。如果其中表达式的值为非0则执行一个if语句体,if语句的语法有两种格式。语法选择语句: if (表达式) 语句 if (表达式) 语句 else 语句在if语句的两种格式中,其中的表达式可以具有除结构外的任何类型,它被求值,包括所有副作用。在语法的第一种格式中,如果表达式为真(非0),则执行其中的语句;如果该表达式为假(0),则忽略该语句。在该语法的第二种格式中,它使用了else,如果表达式的值为假,则执行第二个语句。使用这两种格式,控制都从if语句转向程序中的下一个语句,除了其中语句之一包含一个break、continue或goto之外。

如下是if语句的例子:

if(i>0)
y=x/i;
else
{
x=i;
y=f(x);
}

在这个例子中,如果i大于0则执行y=x/i;语句;如果i小于或等于0,则把i赋给x,且把f(x)赋给y。注意形成if子句的语句以一个分号结尾。当嵌套if语句和else子句时,使用花括号来组合语句。形成一个更清楚的复合语句。如果不出现在括号,编译器通过把else与最近的缺少else的if进行关联来解决模糊性。

if (i>0)        /*没有花括号 */
if(j>i)
x=j;
else x=i;
在这个例子中,else子句与里层的if语句关联。如果i小于或等于0,则没有值赋给x。
if (i>0)
{ /*有花括号*/
if (j>i)
x=j;
}
else
x=j;

在这个例子中里层的if语句用花括号括起来,使else子句与外层if语句匹配。如果i小于或等于0,把i赋给x。


空语句

一个“空语句”是一个仅包含分号的语句。它可以出现在一个语句能够出现的任何地方。

当执行一个空语句时不发生任何事件。编写一个空语句的正确方式是:

语法;

例如do、for、if和while语句需要一个可执行的语句出现在其语句体中,在不需要一个实际的语句体的情况下要求空语句满足语法要求。

正如其它任何C语言一样。你可以在一个空语句之前包括一个标号。为了给一个不是语句的项加上标号时,例如一个复合语句的闭花括号,你可以给一个空语句加上标号,把它直接插入到获得相同效果的项之前。这个例子说明了空语句的作用:

for (i=0;i<10;ling[i++]=0)

;

在这个例子中,for语句的循环表达式line[i++]=0初始化line的开头10个元素为0,该语句作为空语句,不必须有后续语句。


return语句

return语句终止一个函数的执行并将控制返回调用函数。在调用函数中该调用点之后恢复执行。一个return语句也返回一个值给调用函数。

语法

跳转语法

return 表达式opt

如果出现表达式,则把该表达式的值返回给调用函数;如果省略该表达式,该函数的返回值是不确定的。如果出现表达式,该表达式转换成函数返回的类型。如果该函数用返回类型void说明,一个包含表达式的return语句产生一个警告并不对该表达式求值。

如果一个函数定义中没有return语句出现,在被调用函数的最后一个语句执行之后控制自动返回给调用函数,在这种情况下,被调用函数的返回值是不确定的。如果不需要返回值,说明该函数具有void返回类型;否则,缺省的返回类型是int。

很多程序员使用圆括号括起该return语句的表达式参量。但C不需要圆括号。这个例子说明return语句的作用:

void draw(int I,long L);
long sq(int s);
int main()
{
long y;
int x;
y=sq(x);
draw(x,y);
return();
}
long sq(int s)
{
return (s * s);
}
void draw( int I, long L)
{
/*这里的语句定义画图功能 */
return;
}

在这个例子中,main函数调用两个函数:sq和draw。sq函数返回x*x的值到main,这里的返回值赋给y。draw函数说明为一个void函数,并不返回值。试图赋给draw返回值会导致发生一个诊断消息。


switch 语句

switch和case语句帮助控制复杂条件和分支操作。switch语句把控制转向该语句体中的一个语句。

语法

选择语句:

switch(表达式)语句标号语句:

case 常量表达式:语句

default:语句

控制转向与switch(表达式)的值匹配的case常量表达式的语句。switch语句可以包含任何个数的case实例,但在同一个switch语句中不能有两个switch常量具有相同的值。语句体的执行开始于选择的语句并继续执行直到该语句体结束或者直到一个break语句转移控制到该语句体之外。

switch语句的使用通常象这样的:

switch(表达式)

{

说明

case常量表达式

如果表达式等于这个常量表达式的值而执行的语句

break

default:

如果表达式"不等于任何情况常量表达式时而执行的语句"

}

你可以使用break语句终止switch语句中一个特殊情况的处理并跳转到该switch语句的末尾,没有break,该执行继续到下一个case,执行语句直到一个break或到达该语句末尾,在有些情况下,这个继续是希望的。

如果没有case常量表达式等于switch(表达式)的值,则执行default语句。如果省略了default语句且没有case匹配,则不执行switch体中的语句。最多只能有一个default语句,default语句不一定放在最后,可以放在switch语句体的任何地方。实际上,经常放在switch语句开头更有效。一个case或default标号只能出现在一个switch语句内部。

switch表达式和case常量表达式的类型必须是整数。每个case常量表达式的值在语句体中必须是唯一的。

switch语句体的case和default标号仅在最初测试用于确定该语句体的执行开始的位置。

switch语句可以嵌套,在执行转向任何switch语句之前初始化任何静态

变量。

注意:说明可以出现在该复合语句的开头形成switch体,但不执行包括在说明中的初始化。switch语句把控制直接转向该语句体中一个可执行的语句,绕过包含初始化的行。

如下例子说明了switch语句的作用:

switch (c)
{
case ′A′:
capa++;
case ′a′:
lettera++;
default:
total++;
}

本例中switch体的所有三个语句在c等于′A′时都执行,因为在每个case之前没有出现一个break语句。执行控制转向第一个语句(capa++),并继续依次通过语句体中余下的语句。如果c等于′A′,则lettera和total都增1。如果c不等于′A′或′a′,则只有total增1。

switch(i)
{
case -1:
n++;
break;
case 0:
z++;
break;
case 1:
p++;
break;
}

在这个例子中,switch体中每个语句后跟一个break语句。break语句在执行一个语句后强制从该语句体退出。如果i等于-1,只有n增1,跟随n++语句后面的break语句导致执行控制转出该语句体,而避开其余的语句。类似地,如果i等于0,只有z增1;如果i等于1,只有p增1。最后的一个break语句不必要求,因为控制在复合语句的末尾就转出体外,但为了一致性包括了该语句。

单个语句可以加上多个case标号,正如如下例子:

case ′a′:

case ′b′:

case ′c′:

case ′d′:

case ′e′:

case ′f′:hexcvt(c);

在这个例子中,如果常量表达式等于′a′到′f′的任何字母,则调用

hexcvt函数。

Microsoft特殊处

Microsoft C不限制一个switch语句中case子句的个数,其个数仅受限于可用的存储器。

ANSI C在一个switch语句至多允许257个case标号。

Microsoft C缺省能使Microsoft扩充,使用/Za编译器选项禁止这些扩充。

Microsoft特殊处结束


try-except语句

Microsoft特殊处

try-except语句

是C语言的Microsoft扩充,它能使一个应用在出现正常终止执行事件时获取程序控制,这样的事件称为异常,处理异常的机制被称为结构的异常处理。

异常可以基于硬件或基于软件的,甚至在应用不能从硬件或软件异常完全恢复时,结构的异常处理使它可能显示错误信息并捕捉该应用的内部状态帮助诊断新问题。这对于处理不容易重现的间歇性问题特别有用。

语法

try-except语句:

__try复合语句

__except(表达式)复合语句

在__try语句之后的复合语句是保护段。_ _except子句之后的复合语句是异常处理器。该处理器指出在保护段执行期间发生一个异常时所执行的一组动作。执行过程如下:

1. 执行保护段。

2. 如果在保护段执行期间没有异常发生,继续执行__except子句之后的语句。

3. 如果在保护段执行期间出现一个异常或者在保护段调用的例程中出现一个异常,则__except表达式被求值,其返回值确定如何处理异常。这里有三个值:

EXCEPTION_CONTINUE_SEARCH:异常不能识别,继续搜索栈查找一个处理器,首先搜索包含try-except语句的处理器,然后搜索下一个最高优先级的处理器。

EXCEPTION_CONTINUE_EXECUTION:异常被识别但不予考虑。继续在出现异常的点执行。

EXCEPTION_EXECUTE_HANDLER:异常被识别。通过执行__except复合语句把控制转向异常处理器,然后在出现异常的点继续执行。因为__except表达式作为一个C异常被求值,它限制为单个值、条件表达式运算符或逗号运算符。如果需要更广泛的处理,该表达式可以调用一个例程返回上述所列的三个值之一。

注意:结构的异常处理与C和C++源文件一起工作。虽然它不能特别为C++设计,你能够通过使用C++异常处理确保你的代码具有更好的移植性。C++异常处理机制也具有更好的灵活性,它能够处理任何类型的异常。

对于C++程序,C++异常处理可以用于代替结构的异常处理。有关更多信息,参见本书后面“Microsoft Visual C++6.0语言参考手册”部分第5章“语句”中的“异常处理”。

一个应用中的每个例程都有自己的异常处理器。__except表达式在__try语句体的范围内执行。这意味着它可以访问这里说明的任何局部变量。__leave关键字在一个try-except语句块中是有效的。_ _leave的作用是跳到该try-except块的末尾。在该异常处理器结束之后恢复执行。虽然一个goto语句可以用于完成同样的功能,但一个goto语句导致栈的消除内务操作。

__leave语句更有效,因为它不涉及到栈的消除内务操作。

使用longjmp运行函数退出一个try-except语句认为是异常终止。跳进一个__try语句是非法的,但跳出一个_ _try语句是合法的。如果一个进程在执行一个try-except语句中被删除则不调用该异常处理器。

例子

如下是一个异常处理器和一个终止处理器的例子。有关终止处理器的更多信息参见下一节“try-finally语句”。

...puts("hello");

__try {

puts("in try");

__try {

puts("in try");

RAISE_AN_EXCEPTION();

} __finally {

puts("in finally");}

}

__except(puts("in filter"), EXCEPTION_EXECUTE_HANDLER) {

puts("in except");

}

puts("world");

如下是上例的输出结果,在右边加有注释:

hello

in try /*进入try */

in try /*进入嵌套的try*/

in filter /*执行过滤器;因此接受返回1 */

in finally /*消除嵌套的finally*/

in except /*转移控制到选择的处理器 */

world /*退出处理器 */

Microsoft特殊处结束


try-finally语句

Microsoft特殊处

try-finally语句是C语句的Microsoft扩充,能使应用保证在执行一个代码块中断时执行清理(cleanup)代码。清理由这样的功能组成,如取消存储器分配关闭文件和释放文件句柄。try-finally 语句对于这样的例程特别有用,该例程有几个位置检测导致过早地从该例程返回的错误。

语法

try-finally 语句:

__try复合语句

__finally 复合语句

__try子句后的复合语句是保护段。_ _finally子句后的复合语句是终止处理器。该处理器指出在退出保护段时执行的一组动作,无论该保护段是由异常(非正常终止)或由标准下行(正常终止)退出的。

通过简单的顺序执行(下行)控制到达一个__try语句。当控制进入该_ _try语句时,它关联的处理器变成活动的。执行过程如下:

1. 执行保护段。

2. 调用终止处理器。

3. 当终止处理器完成后,继续执行__finally语句之后的语句,无论该保护段如何终止(例如,经由goto语句转出保护段外面或至由一个return语句)在控制流移出保护段之前执行该终止处理器。

__leave关键字在一个try-finally语句块中是有效的。_ _leave的作用是跳转到该try-finally块的末尾,直接执行该终止处理器,虽然一个goto语句可以用于完成相同的功能,但一个goto语句导致栈的消除内务操作。__leave语句更有效,因为它不涉及到栈的消除内务操作。

使用一个return语句或longjmp运行函数退出一个try-finally语句被认为是非正常终止。跳进一个__try语句是非法的,但从中跳转出来是合法的。从启动点到目的之间所有活动的__finally语句必须运行,这称之为“局部展开”。

如果在执行try-finally语句时一个进程被删去,则不调用该终止处理器。

注意:结构的异常处理与C和C++源文件一起工作。虽然它不能特别为C++设计,你能够通过使用C++异常处理确保你的代码具有更好的移植性。C++异常处理机制也具有更好的灵活性,它能够处理任何类型的异常。

对于C++程序,C++异常处理可以用于代替结构的异常处理。有关更多信息,参见本书“Microsoft Visual C++ 6.0语言参考手册”部分第5章“语句”中的“异常处理”。

参见try-except 语句的例子看try-finally语句是如何工作的。

Microsoft特殊处结束


while语句

while语句让你重复一个语句直到指定的表达式变成假。

语法

迭代语句:

while(表达式) 语句

该表达式必须具有算术或指针类型。执行过程如下:

1. 对表达式进行求值。

2. 如果表达式起初为假,不执行while语句体。控制从while语句转向程序中的下一个语句。

如果表达式为真(非0),执行语句体并从第1步开始重复这一过程。

当执行该语句体中的一个break、goto或return时也终止while语句。使用continue语句终止一次迭代而不退出while循环,continue语句把控制转向while语句的下一次迭代。

如下是一个while语句的例子:

while(i>=0)
{
string1[i] = string2[i];
i--;
}

这个例子把字符从string2拷贝到string1。如果i大于或等于0,把string2[i]赋给string[i]并将i减1。当i到达0或小于0时,终止该while语句的执行。

原创粉丝点击