圖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結構。