第三章C語言程序的控製結構(2 / 3)

圖3-12 開關分支的流程圖

【例3-11】根據學生成績的等級打印出分數段。

具體的程序如下:

main( )

{ char grade;

printf("input the grade(A,B,C,D,D):");

scanf(" % c",& grade);

switch(trade)

{case'A':printf("90-100 \ n");break;

case'B':printf("80-89 \ n");break;

case'C':printf("70-79 \ n");break;

case'D':printf("60-69 \ n");break;

case'E':printf("0-59 \ n");break;

default:printf("error \ n");

}

}

在使用switch-case分支時,應注意以下幾點:

(1)switch後麵的表達式可以是整型、字符型或枚舉類型表達式。

(2)case後麵的判斷值要求是一個整常量表達式,它可以是一個整數、字符常量、枚舉常量或整常量表達式。

(3)各分支語句組中的break語句使控製退出switch結構。若沒有break語句,則程序將繼續執行下麵一個case中的語句組。例如:

switch(表達式C)

case 'a':

x++;

case'b'

x+ +

default:

total+ +;

在此開關分支語句中,若表達式C的取值是'a',則三個分支都執行,若C的取值是'b',則隻執行最後兩個分支(即x+ +和total+ +),若C的取值既不是'a'也不是'b',則隻執行total + +。

(4)在開關分支結構中,各個case及default(default)之後有break語句時)的次序是任意的,但各case後的判斷值必須不同。

(5)在開關分支中,default部分不是必須的,如果沒有default部分,則當表達式的值與各case的判斷值都不一致時,則程序不執行該結構中的任何部分。例如:

switch(表達式C)

case 0

x+ =2;

break;

case 1:

y+ =2;

break;

case2:

z+ =2

break;

當C的值既不是0也不是1或2時,則程序不執行該開關分支中的任何語句。為了使程序能進行錯誤檢查或邏輯檢查,應該使用default分支。例如:

switch(ch)

case 'x':printf("YES

");

break;

case' y':printf("NO

");

break;

當ch的值是其他字符時,就會什麼都不輸出。如果加上default分支,且在default分支中輸出一定的提示信息,那麼,遇到default分支時,就可輸出提示信息。

增加default分支會給邏輯檢查帶來很多方便。例如,如果用switch語句來處理數目固定的條件,而且認為這些條件之外的值都屬於邏輯錯誤,則可以增加一個default分支來識別邏輯錯誤。

(6)盡管最後一個分支之後的break語句可以省略,但我們推薦保留它。在最後一個分支之後有break語句是程序設計的好習慣。因為你寫的程序要被你自己或他人維護,例如要在最後一個分支之後增加幾個case分支,如果沒有注意到最後一個分支之後沒有break語句,則原來的最後一個分支就會受新增分支的幹擾而失效。

(7)在switch分支結構中,如果對表達式的多個取值都執行相同的語句組時,則對應的多個case可共同使用同一個語句組。例如在下麵的程序段中,case1、case2和case3均共用case3的語句。

switch(grade)

{case'A':

case 'B':

case 'C':printf("pass!

");break;/ *前三個月case共用一個printf語句。* /

case 'D':printf("NO pass! \ n");break;

default:printf("error

");break;

3.3.6 條件分支程序設計舉例

本小節通過一些實例來進一步說明分支程序設計的方法。

【例3-12】有兩個圓台,圓心的坐標分別是(3,3)、(-3,-3),各圓台的半徑均為1.5,高度分別為10米和5米,圓台以外的地麵的高度均為0米。輸入一個坐標,求此處的高度。

算法設計:判斷輸入的坐標是否在某一個圓台的範圍之內。若在第一個圓台(3,3)之內,則認為此處的高度是10米;在第二個圓台內,認為此處的高度是5米;否則,認為此處的高度是0米。其程序如下:

# include < stdio.h >

# include

main()

{float x,y,dist1,dist2;

printf("input the coordinate:");

scanf(" % f% f,& x,& y);

dist1 =pow((x -3) * (x -3) + (y -3) * (y -3),0.5);

dist2 =pow((x +3) * (x +3) + (y +3) * (y +3),0.5);

if(dist1 < =1.5)

printf("height=10\ n");

else if(dist2 < =1.5)

printf("height = 5\ n");

else

printf("height = 0 \ n");

}

執行結果:

input the coordinate:21<CR>

height=0

input the coordinate:32<CR>

height=10

input the coordinate:-3-1.5<CR>

height=5

【例3-13】請編寫求解一元二次方程根的程序。

算法設計:對於一元二次方程ax2+bx+c=0,輸入a、b、c三個係數後,首先應該判斷:若a等於零,則將方程當作一次方程求解,否則,求解二次方程,具體方法是根據b2-4ac的值大於0,小於0,等於0分別求解。可見整個求解過程將使用一個多重嵌套的if語句。

main()

{

float a,b,cp,rpart,ipart,x1,x2; / * 定義實型變量名 * /

printf(" \ n Enter a,b,c,:"); / * 輸入方程的係數 * /

scanf(" % f% f% f,&a,&b,&c);

if(a= = 0) / * 等於零,則方程為一次方程 * /

{

if(b = =0) / * 判斷b是否等於0 * /

printf(" \ n a,b,c are illegal !");

else

{

x1=- c / b;

printf("There is one root: x = % f",x1);

}

} / * 此方程是一個二次方程 * /

else

{

p =b * b - 4 * a * c;

if(p > = 0)

if(p= = 0)

{

x1 =-b / (2 * a);

printf("double root x = % f",x1);

}

else

{

x1= -b (2 * a)+sqrt(p) / (2 * a);

x2= -b/(2 * a)-sqrt(p) / (2 * a);

printf(" \ n x1 = % f x2 = % f",x1,x2);

}

else

{

rpart = -b / (2 * a);

ipart= sqrt(-p) / (2 * a);

printf(" \ n bas has comphex roots:");

printf("

x1 = % f + % fi",rpart,ipart);

printf("

x2 = % f - % fi",rpart,ipart);

}

}

}

【例3-14】請用switch結構和if-else結構分別編製某運輸公司計算運費的程序。運行程序時,用戶輸入運輸距離和運量,程序將給出單價和總金額。

收費標準為:s<500km時沒有優惠,單價為5元/(噸·公裏);500km≤s<1000km優惠2%;1000km≤s<2000km優惠5%;2000km≤s<3000km優惠8%;3000km≤s優惠10%。

其程序如下:

# include < stdio.h >

main()

{int distance,ton,grade;

float unit_price,total_price;

printf("input the distance and weight:");

scanf(" % d% d,& distance,& 100);

grade = distance/500;

switch(grade)

{

case 0:unit_price=5;break;

case 1:unit_price=5*0.98;break;

case 2:

case 3:unit_price=5*0.95;break;

case 4:

case 5:unit_price=5 *0.92;break;

default:unit_price=5*0.9;break;

}

total_price=unit_price*ton*distance;

printf("the unit price is % .3f.

the total price is %.3f n",unit_price,total_price);

}

此例用了switch結構的分支,當然也可以用如下的if-else分支代替switch:

if(distance<500)

unit_price=5;

else if(distance>=500&distance<1000)

unit_price=5*0.98;

else if(distance>=1000&&distance<2000)

unit_price=5*0.95;

else if(distance>=2000&&distance<3000)

unit_price=5*0.92;

else unit_price=5*0.9;

【例3-15】寫程序,判斷某一年是否閏年。

在本例中,以變量leap代表是否閏年。如果是閏年,令leap=1。非閏年,leap=0。最後判斷leap是否為1(真);若是,則輸出“閏年”信息。

程序如下:

main()

{

int year,leap;

scanf("%d",&year);

if(year%4 = = 0)

{if(year%100 = =0)

{if(year%400 = =0)

leap = 1;

else leap = 0;}

else

leap = 1;}

else

leap = 0;

if(leap)

printf("%d is",year);

else

printf("%d is not",year);

printf("a leap year

");

}

運行情況如下:

(1)2001

2001 is not a leap year

(2)2004

2004 is a leap year

也可以用一個邏輯表達式包含所有的閏年條件,將例子中的if語句用下麵的if語句來代替:

if((year %4 = = 0 && year %100 ! = 0)| |(year % 400 = = 0)) leap = 1;

else leap = 0;

3.4 循環結構程序設計

循環結構是結構化程序設計的基本結構之一,其應用相當廣泛,幾乎所有的應用程序都會用到循環結構。C語言提供了while、do-while和for三種語言來實現循環,下麵分別介紹這三種語言。

3.4.1 while語句

用while語句實現當型循環結構。其算法描述如圖3-13所示。

圖3-13 while的算法描述

其程序形式如下:

while(條件表達式C)

程序段S;

當C取值非0時(即邏輯值為“真”),則執行程序段S,否則跳出循環。程序段S可以是單個語句、空語句或複合語句,稱之為循環體。

【例3-16】使用while循環求兩個正整數的最小公倍數。

其程序如下:

# include < stdio.h >

main()

{int m,n,result;

printf("input two integers:");

scanf(" % d% d",& m,& n);

result=m

while( !(result % m= =0& &result % n= =0))

result+ +;

printf("the lease common multiple of m and n is % d \ n",reault);

}

注意到執行while循環時,是先對條件進行判斷,滿足條件(邏輯值為“真”)則執行循環體,否則跳出循環,這與do-while循環的執行方式是不同的。下麵介紹do-while循環的使用。

3.4.2 do-while語句

do-while語句用於實現直到型循環結構,其算法描述如圖3-14所示。

圖3-14 do-while循環

其程序形式如下:

do{

程序段S;

while(條件表達式C);

程序段S是循環體,它可以是單個語句、空語句或複合語句。

從流程圖中可以看出,do-while循環的執行流程是先執行循環體,再計算表達式C,然後判斷它是否非0,如果非0,則返回重新執行循環體,否則結束循環。所以do-while循環是先執行一次循環體,再判斷是否繼續循環。

【例3-17】使用do-while循環求兩個正整數的最小公倍數。

其程序如下:

# include < stdio.h >

main()

{int m,n,result;

printf("input two integers:");

scanf("% d % d,& m,& n);

result =m > n? m:n;

result - -;

do{

result+ +;

{while(result % m!=0||result % n!=0);

printf("the lease sommon multiple of m and n is % d \ n",result);

}

對比使用while循環和do-while循環求解同樣的問題可以看出,其不同之處主要在對初值的處理上。使用do-while語句時,應該注意以下幾點:

(1)在do-while循環中,while(c)之後的分號(;)不要忘掉。(在while循環中,while(c)之後是沒有分號的。)

(2)在do-while循環中,不管循環體是否為單一語句,習慣上都用花括號把它括起來,並把“while(c);”直接寫在“|”的後麵,以免把“while(c):”部分誤認為是一個新的while循環的開始。

【例3-18】用do-while語句求。

程序如下:

main()

{

int i,sum=0;

i=1;

do

{sum=sum+i;

i++;

}

while (i<=100);

printf("%d",sum);

}

可以看到,對同一個問題可以用while語句處理,也可以用do-while語句處理。do-while結構可以轉換成while結構。可見,do-while結構是由一個語句加一個while結構構成的。

在一般情況下,用while語句和用do-while語句處理同一問題時,若二者的循環體部分是一樣的,它們的結果也一樣。但在while後麵的表達式一開始就為假(0值)時,兩種循環的結果是不同的。

【例3-19】while和do-while循環的比較。

(1)main() (2)main()

{int sum=0,i; {int sum=0,i;

scanf("%d",&i); scanf("%d",&i);

while(i<=10) do

{sum=sum+i; {sum=sum+i;

i++; i++;

} }

while(i<=10);

printf("%d",sum); printf("%d",sum);

} }

運行情況如下: 運行情況如下:

1↙ 1↙

55 55

可以看到:當輸入i的值小於或等於10時,二者得到結果相同。而當i>10時,二者結果就不同了。這是因為此時對while循環來說,一次也不執行循環體(表達式“i<=10”為假),而對do-while循環來說則要執行一次循環體。可以得到結論:當while後麵的表達式的第一次的值為“真”時,兩種循環得到的結果相同。否則,二者結果不相同(指二者具有相同的循環體的情況)。

還要注意一點:do-while循環是先執行循環體語句,後判斷表達式,從這點來說,它類似於直到型循環,但它與其他語言中的until型循環(不同,do-while循環是當表達式為真時反複執行循環體,表達式為假時結束循環。而典型的until(直到型)循環結構則是表達式為真時結束循環。

3.4.3 for語句

循環結構中,for語句的功能比while語句更強,使用也更為靈活方便。原則上,所有的while循環都可以用for循環來替代。for循環的處理流程如圖3-15所示。

圖3-15 for循環

程序形式為:

for(語句S1;條件表達式C;語句S2)

程序段S;

語句S1、S2,條件表達式C的作用各不相同。原則上說,S1和S2可以是一般的語句,但實際上S1常常是一個在初次進入循環之前給某些變量賦值的表達式,也叫初值表達式;S2常常是一個在每次執行循環體後對某些變量進行修改的表達式,也叫修改表達式。條件表達式C用於指定循環條件,其作用同while語句中的表達式C相同,它通常是關係表達式或邏輯表達式。S1、S2、C中的任何一個均可省略,但其後麵的分號決不能省略。如果省略C,則循環條件將恒為“真”。例如:

for(::)

}

上述結構是下一個無限循環,需要通過其他手段(比如break語句)結束循環。

程序段S是循環體,它可以是單個語句、空語句或複合語句,如果是單個語句或空語句,可以省去花括號。如果循環體是由多個語句組成,則必須用花括號括起來。

for循環的執行流程如下:

(1)執行語句S1。

(2)計算機表達式C,判斷其值,若為“真”(即非0),則執行(3);若為“假”(即0),則結束循環。

(3)執行循環體S。

(4)執行語句S2。

(5)重複執行(2)。

【例3-20】利用for循環計算:12+22+32+42+…202

其程序如下:

# # include < stdio.h >

main()

{int k;

lont result=0;

for(k=1;k< =20;k+ +)

result+ =k * k;

printf("the result: %ld \ n",result);

}

for循環語句和與之等價的while循環語句的對應關係如下:

for語句 對應的while語句

for(S1;C;S2) S1;

S; while(C)

{

} S;

S2

3.4.4 三種循環的比較

C語言提供的三種循環語句可以用來處理同一問題,一般情況下可以互換。但其功能和靈活程度不同,對這三種循環的比較如下:

(1)for語句功能最強,最方便靈活,使用最多,任何循環都可以用for實現。while和do-while用的最少(但這並非它沒用,對某種情況它還是很有價值的)。

(2)while和do-while的循環變量初始化是在循環語句之前完成,而for語句循環變量的初始化是在for中的S1語句(常常是一個表達式)中實現。

(3)for循環中的第一個和第三個語句(S1、S2)可以是逗號表達式,這是for語句的一個很有用的特性。它擴充了for的作用範圍,使它有可能同時對若幹參數(如循環變量,重複計算參數等)進行初始化和修改等。

(4)for和while循環是先判斷循環條件,後執行循環體;而do-while循環則是先執行一次循環體,然後才判斷循環條件。因此,後者不管什麼情況下,都至少要執行一次循環體。

【例3-21】判斷某字符串是否是前後對稱的。

其程序如下:

# include < stdio.h >

# include

main()

{int j,k,sign;

char str[50];

printf("input a str;");

scanf("% s",str);

for(j=0,k=strlen(str)-1,sign=0;k>j;j+ +,k- -)

if(str[j]!=str[k])

sign=1;

if(sign= =1)

printf("not a symmetrical string. \ n");

else

printf("a symmetrical string. \ n");

}

該例中用到了數組,其中s[i]表示數組中的第i個元素。s[i]=s[j]表示把數組中第j個元素中的字符賦給數組中的第i個元素。有關數組的概念請參看第四章。在程序中,由於可以用逗號表達式,就使得循環靈活多了。

3.4.5 多重循環

在一個循環的循環體內又包含另一個循環,這稱之為循環的嵌套。被嵌入的循環又可以嵌套循環,這就是多重嵌套,又稱作多重循環。在實際應用中,經常要用到多重循環。

C語言提供的三種循環語句都可以互相嵌套。

在使用循環相互嵌套時,被嵌套的一定是一個完整循環結構,即兩個循環結構不能相互交叉。如圖3-16所示,其中(a)是非法的嵌套,(b)是合法的嵌套。

(a) (b)

圖3-16 循環的合法與非法嵌套

下麵是一個多重循環的例子。

【例3-22】有一個數43634,其左右對稱,求比它大的對稱數中最小的那一個。

本程序使用兩重嵌套循環,外層循環用於由小到大逐一列舉比43635大的整數,直到找到一個對稱數,內層循環用於判斷列舉到的每一個是否是對稱的,具體方法是將此整數的每一位分離出來進行比較。

其程序如下:

# include < stdio.h >

main()

{long int i=43634,j;

int count,ch[5],sign=0;

do|i+ +;

j=i;

count=0;

while(j)

{ch[count]=j% 10;

j=j/10;

count+ +;

}

if(ch[0]= =ch[4]& &ch[1]= =ch[3])

sign=1;

}while(! sign);

printf("% ld \ n",i);

}

3.4.6 循環和開關(switch)分支的中途退出

前麵我們討論了C語言提供的三種循環,它們都是根據循環判斷表達式為0時來控製循環結束,這種結束是正規的循環結束。在實際應用中,往往還要求在循環的中途退出循環,這是一種非正規的循環退出。實現非正規循環退出的語句有break和continue。

1.break語句

break語句的功能是退出當前循環或當前switch結構。如果break語句處在一個循環體中,則執行它時退出當前所在的循環,即結束當前層循環。如果break語句處在switch語句的一個分支中,則執行它時退出當前所在的switch結構。