c语言流程控制的三种基本结构,c语言程序流程控制的三个常用结构
C语言系列:3、过程控制文章目录C语言系列:3、流程控制1。语句和块2。if-else语句3。else-if语句4。转换语句5。while循环和for循环6。do-while循环7。中断语句和继续语句8。转到语句和标签1。语句和块添加分号(;),他们成为
声明。例如:
x=0;
我;
printf(.);在C语言中,分号是一个语句的结束,而Pascal等语言使用分号作为语句之间的分隔符。
一组声明和语句用一对大括号“{”和“}”括起来,形成一个复合语句(也称为
程序块),复合语句在语法上等同于单个语句。函数体中用花括号括起来的语句就是一个明显的例子。
If、else、while和for后面用花括号括起来的几个语句都是类似的例子。右花括号用于结束代码块,其后不需要分号。
2.if-else语句if-else语句用于条件判断,其语法如下:
如果{表情}
1项声明
其他
2语句,其中else部分是可选的。语句执行时,先计算表达式的值,如果其值为真(即表达式的值不为0),则执行语句1;如果其值为false(即表达式的值为0),并且语句包含else部分,则执行语句2。
因为if语句只是一个简单的测试表达式,所以可以简化一些代码的编写。最明显的例子写如下
If(表达式)代替
如果(表情!0)在某些情况下,这种形式自然是清楚的,但在另一些情况下,意思可能就不清楚了。
因为if-else语句的else部分是可选的,所以在嵌套的if语句中省略它的else部分会导致歧义。解决方案是,如果没有else对,则将else与最近的前一个匹配。
例如,在下面的语句中:
如果(n ^ 0)
如果(a b)
z=a;
其他
z=b;else部分匹配内层的if,从程序的缩进结构也能看出来。如果这不符合我们的意图,我们必须使用花括号来强制正确的匹配关系:
if (n 0) {
如果(a b)
z=a;
}
其他
z=b;在以下情况下,模糊尤其有害:
如果(n ^ 0)
for(I=0;I n;我)
if (s[i] 0) {
printf( . ));
返回I;
}
else /*错误*/
printf(error - n为负\ n );程序的缩进结构清楚地表明了设计意图,但编译器无法获得这些信息,它会将else部分与内层的if配对。这种错误很难发现,所以我们建议在有嵌套if语句的情况下使用花括号。
顺便提醒读者,在声明中
如果(a b)
z=a;
其他
z=b;z=a后面是分号。这是因为,从语法上讲,if后面应该跟一个类似“z=a;这个类的表达式语句总是以分号结尾。
3.else-if语句在C语言中,我们经常使用以下结构:
If(表达式)
句子
Else if(表达式)
句子
Else if(表达式)
句子
Else if(表达式)
句子
Else,这里就单独解释一下吧。这种if语句序列是编写多路径决策最常用的方法。将依次计算的表达式。一旦表达式的结果为真,相关的语句将被执行,整个语句序列的执行将被终止。类似地,每个语句可以是单个语句,也可以是用花括号括起来的复合语句。
最后一个else部分用来处理“以上条件都不成立”的情况或者默认情况,即以上条件都不满足的情况。有时,没有必要对默认值执行显式操作。在这种情况下,您可以将
Else部分省略;这个部分也可以用来检查错误,以捕捉“不可能”的情况。
这里,用一个二分搜索法函数来说明三向判断程序的用法。这个函数用于确定一个特定的值x是否存在于排序后的数组v中。数组v的元素必须按升序排列。如果x包含在v中,函数返回x在v中的位置(0到n-1之间的整数);否则,该函数返回-1。
对半查找时,首先将输入值X与数组v的中间元素进行比较,如果X小于中间元素的值,则在数组的前半部分查找;否则,在数组的后半部分寻找。在这两种情况下,下一步是将X与所选零件的中间元素进行比较。这个过程一直持续到找到指定的值或者搜索范围为空。
/* binsearch:在v[0]=v[1]=中查找x.=v[n-1] */
int binsearch(int x,int v[],int n)
{
int低、高、中;
低=0;
高=n-1;
while(低=高){
mid=(低高)/2;
if (x v[mid])
高=中1;
else if (x v[mid])
低=中1;
else /*找到匹配项*/
返回mid
}
return-1;/*不匹配*/
}这个函数的基本判断是:在每一步判断x是否小于、大于或等于中间元素v[mid]。使用
else-if结构做出这个决定是很自然的。
4.switch语句switch语句是一个多通道的判断语句,测试表达式是否匹配某个常量整数值的某个值,并执行相应的分支动作(常量整数值使switch呈现鸡肋,这一块在Go中进行了优化)。
开关(表达式){
Case表达式:语句序列
Case表达式:语句序列
默认值:语句序列
}每个分支由一个或多个整数值常量或常量表达式标记。如果一个分支匹配表达式的值,它将从该分支执行。分支表达式必须互不相同。如果没有分支可以匹配表达式,则执行标记为默认的分支。默认分支是可选的。如果没有默认分支,也没有其他分支与表达式的值匹配,则switch语句不执行任何操作。每个分支和默认分支的顺序是任意的。
我们使用if…else if…else结构编写了一个程序来计算每个数字、空格和所有其他字符的出现次数。让我们用switch语句重写程序,如下所示:
#包含stdio.h
main() /*计数位数、空格、其他*/
{
int c,I,nwhite,nother,ndigit[10];
n white=nother=0;
for(I=0;i 10我)
ndigit[I]=0;
while ((c=getchar())!=EOF) {
开关(c) {
大小写“0”:大小写“1”:大小写“2”:大小写“3”:大小写“4”:
大小写“5”:大小写“6”:大小写“7”:大小写“8”:大小写“9”:
ndigit[c- 0 ];
打破;
案例“”:
大小写 \n :
大小写 \t :
n白色;
打破;
默认值:
其他;
打破;
}
}
printf( digits=);
for(I=0;i 10我)
printf( %d ,ndigit[I]);
printf(,空白=%d,其他=%d\n ,nwhite,nother);
返回0;
}break语句将导致程序的执行立即从switch语句中退出。在switch语句中,case
的作用只是一个标签。因此,一个分支中的代码执行完之后,程序会在下一个分支中继续执行,除非在程序中显式跳转。跳出switch语句最常见的方法是使用break语句和return语句。break语句还可以强制控制立即退出while、for和do循环语句,我们将在后面进一步介绍。
依次执行每个分支的做法有利也有弊。从好的方面来说,它可以组合几个分支来完成一项任务,比如上面例子中对数字的处理。但是,在正常情况下,为了防止直接执行下一个分支,每个分支都必须以break语句结束。从一个分支到下一个分支并不完美,修改程序时很容易出错。除了计算需要多个标签的情况之外,应该尽量减少直接从一个分支到下一个分支,并且在必须使用时应该添加适当的程序注释。
作为一种好的编程风格,在switch语句的最后一个分支(默认分支)之后
还要添加一个break语句。这在逻辑上是不必要的,但当我们需要在switch语句后添加其他分支时,这种预防措施会降低出错的可能性。
5.while循环语句中的while循环和for循环
While(表达式),先求表达式的值。如果其值不为0,则执行该语句并再次计算表达式。这个循环过程一直持续到表达式的值为0,然后继续执行语句的后面部分。
For循环语句:
For(表达式1;表情2;3)它等效于下面的while语句:
表情1;
While(表达式2) {
句子
表情3;
}但当while或for循环语句包含continue语句时,上述两个语句不一定等价。
从语法的角度来看,for循环语句的三个组成部分是表达式。最常见的是,表达式1和3是赋值表达式或函数调用,表达式2是关系表达式。这三个部分中的任何一个都可以省略,但是分号必须保留。如果在for语句中省略了表达式1和3,它就会退化为while循环语句。如果测试条件(表达式2)被省略,它的值总是被认为是真值。因此,下面的for循环语句:
for(;) {
.
}是一个“无限”循环语句,需要其他手段(如break语句或return语句)来终止其执行。
设计程序时,选择while循环语句还是for循环语句,主要取决于程序员的个人喜好。例如,在下面的语句中:
while((c=getchar())== c== \ n c= \ t )
;/*跳过空格字符*/因为里面没有初始化或重新初始化,所以使用whi1e loop语句更自然。
如果语句中需要简单的初始化和变量增量,使用for语句更合适,它将循环控制语句集中在循环的开始,使结构更加紧凑清晰。这可以从下面的声明中清楚地看出:
for(I=0;I n;我)
.这是C语言处理数组前N个元素的习惯用法。它类似于Fortran语言中的DO循环或Pascal语言中的for循环。但这种类比并不完全准确,因为在C语言中,for循环语句的循环变量和上限可以在循环体中修改,循环变量I的值在循环因故终止后仍然保留。因为for语句的每个组成部分可以是任何表达式,所以for语句不限于通过算术级数进行循环控制。但把一些无关紧要的计算放在for语句的初始化和变量增量部分,是不良的编程风格,更适合循环控制操作。
举个大例子,让我们重写函数atoi,将字符串转换成相应的数值。
以下是程序的结构,从中可以看出输入的格式:
如果有空白字符,跳过它。
如果有符号,请阅读符号。
取整数部分,并执行转换。转换的每一步都会相应地处理输入数据,并为下一步做准备。当遇到第一个不能转换成数字的字符时,整个过程终止。
#包含ctype.h
/* atoi:将s转换为整数;版本2 */
int atoi(char s[])
{
int i,n,sign
for(I=0;is space(s[I]);i ) /*跳过空白*/
;
sign=(s[i]==-)?-1 : 1;
if (s[i]== s[i]==-) /*跳过符号*/
我;
for(n=0;is digit(s[I]);我)
n=10 * n(s[I]- 0 );
回车符* n;
}标准库中提供了更完善的函数strtol,将字符串转换为长整数。有关strtol函数的详细信息,请参见附录B.5。
对于多个嵌套循环,将循环控制部分集中在一起具有更明显的优势。下面的函数是一个Shell排序算法,用于对整数数组进行排序。Shell排序算法是由D. L. Shell于1959年发明的。它的基本思想是:先比较较远的元素,而不是像简单的交换排序算法那样先比较相邻的元素。这样可以快速减少大量无序的情况,从而缓解后续工作。被比较元素之间的距离逐渐减小,直到减小到1,此时排序就变成了相邻元素的交换。
/* shellsort: sort v[0].v[n-1]按升序排列*/
void shellsort(int v[],int n)
{
int gap,I,j,temp
for(gap=n/2;差距0;间隙/=2)
for(I=gap;I n;我)
for(j=I-gap;j=0v[j]v[j gap];j-=间隙){
temp=v[j];
v[j]=v[j gap];
v[j gap]=temp;
}
}此函数包含一个三重嵌套的for循环语句。最外层的for语句控制两个比较元素之间的距离,从n/2开始,逐渐对折,直到距离为0。中间层的for循环语句用于在元素之间移动位置。最里面的for语句用于比较由间隔位置分隔的元素对,当两个元素顺序相反时,它们会互换。由于gap的值最终将减少到1,所有元素最终都将处于正确的排序位置。注意,即使最外层for循环的控制变量不是等差数列,for语句的书写形式也保持不变,可见for语句具有很强的通用性。
逗号“,”也是C语言中优先级最低的运算符,常用于for语句中。被戏弄
由#分隔的一对表达式将从左到右求值,表达式右边的操作数的类型和值就是其结果的类型和值。这样,在一个for循环语句中,可以在每个语句组件中放置多个表达式,例如,可以同时处理两个循环控制模糊。我们可以以下面的函数reverse(s)为例。这个函数用于反转字符串s中每个字符的位置。
#包含字符串. h
/* reverse:原地反转字符串*/
作废冲销(字符s[])
{
int c,I,j;
for (i=0,j=strlen(s)-1;I j;我,j - ) {
c=s[I];
s[I]=s[j];
s[j]=c;
}
}有些情况下逗号不是逗号运算符,比如逗号分隔函数参数,逗号分隔声明中的变量等。这些逗号并不保证表达式是从左向右计算的。
逗号运算符应该谨慎使用。逗号运算符最适合于紧密相关的结构,比如上面reverse函数中的for语句,也适合于单个表达式中需要多步计算的宏。逗号表达式也适用于反向函数中的元素交换,这样元素交换就可以看作是单步操作。
for (i=0,j=strlen(s)-1;I j;我,j -)
c=s[i],s[i]=s[j],s[j]=c;6.do-while循环while和for这两个循环在执行循环体之前测试终止条件。相反,C语言中的第三个循环——do-while循环在执行完循环体后测试终止条件,使循环体至少执行一次。
do-while循环的语法如下:
做
句子
While(表情);在这种结构中,首先执行循环体中的语句部分,然后计算表达式的值。如果表达式的值为true,则再次执行该语句,依此类推。当表达式的值变为false时,循环终止。除了条件测试的语义不同,do-while循环等价于Pascal中的repeat-until语句。
经验表明,do-while循环比while循环和for循环使用得少得多。尽管如此,做一会儿
陈述有时是有用的。让我们用函数itoa来说明这一点。Itoa函数是atoi函数的反函数,将数字转换成字符串。这项工作比原先想象的更复杂。如果根据atoi函数中生成数字的方法将数字转换为字符串,则生成的字符串的顺序正好相反。因此,我们应该首先以相反的顺序生成字符串,然后反转字符串。
/* itoa:将n转换为s中的字符*/
void itoa(int n,char s[])
{
int i,sign
if ((sign=n) 0) /*记录sign */
n=-n;/*使n为正*/
I=0;
是否{ /*以逆序生成数字*/
s[I]=n % 10“0”;/*获取下一个数字*/
} while((n/=10)0);/*删除它*/
如果(符号0)
s[I]=-;
s[I]= \ 0 ;
反转;
}这里有必要用do-while语句,至少用do-while语句会更方便,因为即使n的
值为0时,数组s中至少要放一个字符,其中,do-while语句体中只有一个语句。尽管这是不必要的,我们仍然用花括号将语句括起来,这可以防止草率的读者将while部分误认为另一个while循环的开始。
7.有时,break语句和continue语句不通过循环头或循环尾的条件测试就跳出循环是很方便的。Break语句可用于提前退出for、while和do-while等循环,就像提前退出switch语句一样。Break语句可以使程序立即跳出switch语句或最里面的循环。
下面的函数trim用于删除字符串末尾的空格字符、制表符和换行符。当发现最右边的字符是非空格、非制表符和非换行符时,使用break语句退出循环。
/* trim:删除尾随空格、制表符、换行符*/
int trim(char s[])
{
int n;
for(n=strlen(s)-1;n=0;n -)
if (s[n]!= s[n]!=\t s[n]!=\n )
打破;
s[n 1]= \ 0 ;
返回n;
}strlen函数返回字符串的长度。for循环从字符串的末尾开始,以相反的方向扫描,查找第一个不是空格字符、制表符或换行符的字符。当找到满足条件的第一个字符时,或者当循环控制变量n变为负数时(即扫描整个字符串时),循环终止执行。读者可以验证这个函数是正确的,即使字符串是空的或者只包含空白字符。
continue语句与break语句相关联,但不如break语句常见。继续语言
句子用来使for、while或do-while语句开始下一个循环的执行。While和do-while
语句,continue语句的执行意味着测试部分被立即执行;在for循环中,意味着控制转移到增量循环变量部分。continue语句仅用于循环语句,不用于switch语句。循环中包含的switch语句中的continue语句将导致下一个循环。
例如,下面的程序用于处理数组a中的非负元素。如果元素的值为负,则跳过处理。
for(I=0;I n;我)
if (a[i] 0) /*跳过负元素*/
继续;
./* do positive elements */当循环的后半部分比较复杂时,经常使用continue语句。在这种情况下,如果不使用continue语句,可能需要反向测试或者缩进另一层循环,这样会使程序的嵌套更深。
8.goto语句和标签C语言提供了可以随意滥用的goto语句和标记跳转位置的标签。从理论上讲,goto语句是不必要的,在实践中,不使用goto语句也很容易编写代码。
但是,在某些情况下,goto语句仍然有用。最常见的用法是在一些深度嵌套的结构中终止程序的处理,比如一次跳出两个或多个循环。在这种情况下,break语句无法实现目标。它只能从最里面的循环退出到下一个更高级别的循环(其他一些语言使用名称空间来处理这种情况)。以下是使用goto语句的示例:
对于(.)
对于(.) {
.
如果(灾难)
goto错误;
}
.
错误:
/*收拾残局*/在这个例子中,如果错误处理代码很重要,错误可能出现在多个地方,那么使用goto语句会更方便。
标签的名称与变量的名称相同,后跟一个冒号。该标签可以位于函数中相应goto语句所在的任何语句之前。标签的范围是整个函数。
让我们看另一个例子。考虑确定两个数组A和B是否具有相同元素的问题。一个可能的解决方案是:
for(I=0;I n;我)
for(j=0;j m;j)
if (a[i]==b[j])
找到goto
/*找不到任何公共元素*/
.
找到:
/*得到一个:a[i]==b[j] */
.所有使用goto语句的程序代码都可以重新编写成没有goto语句的程序,但可能会添加一些额外的重复测试或变量。例如,上述确定是否具有相同数组元素的程序段可以重写为以下形式:
发现=0;
for(I=0;我在!找到了;我)
for(j=0;j . m!找到了;j)
if (a[i]==b[j])
发现=1;
如果(找到)
/*得到一个:a[i-1]==b[j-1] */
.
其他
/*找不到任何公共元素*/
.在大多数情况下,使用goto语句的程序段比不使用goto语句的程序段更难理解和维护,除了少数情况,比如我们前面提到的例子。虽然问题不太严重,但我们还是建议尽量少用goto语句。
否则将追究法律责任。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。