C語言入門系列之5.循環控制結構程序

一、概述

循環結構是程序中一種很重要的結構。
其特點是:在給定條件成立時,反覆執行某程序段,直到條件不成立爲止。
給定的條件稱爲循環條件,反覆執行的程序段稱爲循環體。C語言提供了多種循環語句,可以組成各種不同形式的循環結構:

  • goto語句和if語句構成循環;
  • while語句;
  • do-while語句;
  • for語句。

二、goto語句和用goto語句構成循環

goto語句是一種無條件轉移語句,與BASIC中的goto語句相似。
goto語句的使用格式爲:

goto  語句標號;

其中標號是一個有效的標識符,這個標識符加上一個冒號:一起出現在函數內某處, 執行goto語句後,程序將跳轉到該標號處並執行其後的語句。
另外標號必須與goto語句同處於一個函數中,但可以不在一個循環層中。
通常goto語句與if條件語句連用, 當滿足某一條件時,程序跳到標號處運行。
要注意,通常不用goto語句,主要因爲它將使程序層次不清,且不易讀,但在多層嵌套退出時, 用goto語句則比較合理。

練習:
用goto語句和if語句構成循環求1-100所有數之和。
代碼如下:

#include <stdio.h>

int main(){
	int i = 1, sum = 0;
	loop: if(i <= 100){
		sum += i;
		i++;
		goto loop;
	}
	printf("Sum=%d", sum);

	return 0;
}

打印:

Sum=5050

顯然,此時求出了1-100的和。

三、while語句

1.基本使用

while語句的一般形式爲:

while(表達式)語句

其中表達式是循環條件,語句爲循環體。
while語句的語義是:
計算表達式的值,當值爲真(非0)時, 執行循環體語句。
其執行過程如下:
while流程圖
顯然可以得到,如果表達式的值一開始就爲0,則語句一次也會被不執行。

練習:
用while語句求和1-100。
代碼如下:

#include <stdio.h>

int main(){
	int i = 1, sum = 0;
	while(i <= 100){
		sum += i;
		i++;
	}
	printf("Sum=%d", sum);

	return 0;
}

打印:

Sum=5050

顯然,結果與之前的相同。

可能會出現死循環,示例如下:

#include <stdio.h>

int main(){
	int i = 1, sum = 0;
	while(i <= 100)
		sum += i;
		i++;
	printf("Sum=%d", sum);

	return 0;
}

只是在前面的基礎上去掉了大括號,再運行會發現程序堵塞,一直不輸出結果,查看CPU佔用情況會發現編譯器佔用了大量的CPU,此時產生了死循環,因爲去掉大括號後,while循環默認只包括趕緊跟在其後的第一條語句,即sum += i;,而不再執行i自增,所以i一直爲1,while循環的條件i <= 100的條件恆成立,所以循環一直進行下去,永不結束,此時只能強制關閉程序才能終止運行。

再做一個練習:
統計從鍵盤輸入一行字符的個數。
代碼如下:

#include <stdio.h>

int main(){
	int n = 0;
	printf("Input a string:\n");
	while(getchar() != '\n'){
		n++;
	}
	printf("count=%d", n);

	return 0;
}

打印:

Input a string:
123abc@#$,.?
count=12

本程序中的循環條件爲getchar()!='\n',其意義是, 只要從鍵盤輸入的字符不是回車就繼續循環,循環體n++完成對輸入字符個數計數,從而實現了對輸入字符的計數。

2.注意事項

(1)while語句中的表達式一般是關係表達或邏輯表達式,只要表達式的值爲真(非0)即可繼續循環。
練習如下:

#include <stdio.h>

int main(){
	int a = 0, n;
	printf("Input a number:\n");
	scanf("%d", &n);
	while(n--){
		printf("%d ", a++*2);
	}

	return 0;
}

打印:

Input a number:
10
0 2 4 6 8 10 12 14 16 18

此時,只要n不等於0,循環會一直進行。

(2)循環體如包括有一個以上的語句,則必須用{}括起來,組成複合語句,建議只有一條語句時也用大括號括起來。

四、do-while語句

do-while語句的一般形式爲:

do
    語句
while (表達式);

其執行過程如下:
do-While流程圖

這個循環與while循環的不同在於:
它先執行循環中的語句,然後再判斷表達式是否爲真,如果爲真則繼續循環;如果爲假,則終止循環。
顯然,do-while循環至少要執行一次循環語句。

用do-while循環求和測試如下:

#include <stdio.h>

int main(){
	int i = 1, sum = 0;
	do{
		sum += i;
		i++;
	}
	while(i <= 100);
	printf("Sum=%d", sum);

	return 0;
}

運行結果與之前相同。

如果最開始的條件就爲假,則while循環一次都不會執行,do-while循環會執行一次,這是while循環和do-while循環的區別之一。

五、for語句

1.基本使用

在C語言中,for語句使用最爲靈活,它完全可以取代while語句。
它的一般形式爲:

for(表達式1;表達式2;表達式3) 語句

其執行過程如下:

  • (1)先求解表達式1。
  • (2)求解表達式2,若其值爲真(非0),則執行for語句中指定的內嵌語句,然後執行下面第(3)步;若其值爲假(爲0),則結束循環,轉到第(5)步。
  • (3)求解表達式3。
  • (4)轉回上面第(2)步繼續執行。
  • (5)循環結束,執行for語句下面的一個語句。

for語句最簡單也是最容易理解的形式如下:

for(循環變量賦初值;循環條件;循環變量增量) 語句

循環變量賦初值總是一個賦值語句,它用來給循環控制變量賦初值;
循環條件是一個關係表達式,它決定什麼時候退出循環;
循環變量增量,定義循環控制變量每循環一次後按什麼方式變化。
這三個部分之間用;分開。

求和用for循環改變如下:

#include <stdio.h>

int main(){
	int i, sum = 0;
	for(i = 1; i <= 100; i++){
		sum += i;
	}
	printf("Sum=%d", sum);

	return 0;
}

運算結果與前面相同。
先給i賦初值1,判斷i是否小於等於100, 若是則執行語句,之後值增加1,再重新判斷, 直到條件爲假,即i>100時,結束循環。

2.注意事項

(1)for循環中的表達式1(循環變量賦初值)表達式2(循環條件)表達式3(循環變量增量) 都是選擇項, 即可以缺省,但;不能缺省。

(2)省略了表達式1(循環變量賦初值),表示不對循環控制變量賦初值。

上面代碼也可以寫成:

#include <stdio.h>

int main(){
	int i = 1, sum = 0;
	for( ; i <= 100; i++){
		sum += i;
	}
	printf("Sum=%d", sum);

	return 0;
}

效果一樣。

(3)省略了表達式2(循環條件),則不做其他處理時便成爲死循環。
例如

for(i=1;;i++)
{
    sum=sum+i;

即是一個死循環。

(4)省略了表達式3(循環變量增量), 則不對循環控制變量進行操作,這時可在語句體中加入修改循環控制變量的語句。

代碼還可以寫成:

#include <stdio.h>

int main(){
	int i, sum = 0;
	for(i = 1; i <= 100; ){
		sum += i;
        i++;
	}
	printf("Sum=%d", sum);

	return 0;
}

(5)還可以省略了表達式1(循環變量賦初值)表達式3(循環變量增量)

代碼還可以改成:

#include <stdio.h>

int main(){
	int i = 1, sum = 0;
	for( ; i <= 100; ){
		sum += i;
	}
	printf("Sum=%d", sum);

	return 0;
}

(6)3個表達式都可以省略。
例如for( ; ; )語句相當於while(1)語句。

(7)表達式1可以是設置循環變量的初值的賦值表達式,也可以是其他表達式。

代碼還可以寫爲:

#include <stdio.h>

int main(){
	int i = 1,sum;
	for(sum = 0 ; i <= 100; ){
		sum += i;
	}
	printf("Sum=%d", sum);

	return 0;
}

(8)表達式1和表達式3可以是一個簡單表達式也可以是逗號表達式。
例如:

for(i=0,j=100; i<=100; i++,j--)
    k=i+j;

求和代碼還可以寫爲:

#include <stdio.h>

int main(){
	int i, sum;
	for(i = 1, sum = 0 ; i <= 100; ){
		sum += i;
	}
	printf("Sum=%d", sum);

	return 0;
}

(9)表達式2一般是關係表達式或邏輯表達式,但也可是數值表達式或字符表達式,只要其值非零,就執行循環體。
例如for(i=0;(c=getchar())!=’\n’;i+=c);

3.循環的嵌套

循環的嵌套是指循環中的語句又是一個循環。

練習如下:

#include <stdio.h>

int main(){
	int i, j, k;
	for(i = 1; i <= 3; i++){
		for(j = 1; j <= 4; j++){
			for(k = 1; k <= 5; k++){
				printf("%d, %d, %d\n", i, j, k);
			}
		}
	}

	return 0;
}

打印:

1, 1, 1
1, 1, 2
1, 1, 3
1, 1, 4
1, 1, 5
1, 2, 1
1, 2, 2
1, 2, 3
1, 2, 4
1, 2, 5
1, 3, 1
1, 3, 2
1, 3, 3
1, 3, 4
1, 3, 5
1, 4, 1
1, 4, 2
1, 4, 3
1, 4, 4
1, 4, 5
2, 1, 1
2, 1, 2
2, 1, 3
2, 1, 4
2, 1, 5
2, 2, 1
2, 2, 2
2, 2, 3
2, 2, 4
2, 2, 5
2, 3, 1
2, 3, 2
2, 3, 3
2, 3, 4
2, 3, 5
2, 4, 1
2, 4, 2
2, 4, 3
2, 4, 4
2, 4, 5
3, 1, 1
3, 1, 2
3, 1, 3
3, 1, 4
3, 1, 5
3, 2, 1
3, 2, 2
3, 2, 3
3, 2, 4
3, 2, 5
3, 3, 1
3, 3, 2
3, 3, 3
3, 3, 4
3, 3, 5
3, 4, 1
3, 4, 2
3, 4, 3
3, 4, 4
3, 4, 5

練習:
輸出以下圖形:
循環嵌套練習

代碼如下:

#include <stdio.h>

int main(){
	int i, j;
	for(i = 1; i <= 6; i++){
		for(j = 1; j <= i; j++){
			putchar('*');
		}
		printf("\n");
	}

	return 0;
}

打印:

*
**
***
****
*****
******

顯然,得到了想要的結果。

六、幾種循環方式的比較

  1. 四種循環都可以用來處理同一問題,一般情況下它們可以互相代替。
    一般不提倡用goto型循環。

  2. 在while循環和do-while循環中,只在while後面的括號內指定循環條件,因此爲了使循環能正常結束,應在循環體中加入使循環趨於結束的語句(如i++,或i=i+1等)。
    for循環可以在表達式3中包含使循環趨於結束的操作,甚至可以將循環體中的操作全部放到表達式3中。
    因此for語句的功能更強,凡是用while循環能完成的,用for循環都能實現。

  3. 用while和do-while循環時,循環變量初始化的操作應在while和do-while語句之前完成,而for語句可以在表達式1中實現循環變量的初始化。

  4. while循環、do-while循環和for循環,可以用break語句跳出循環,用continue語句結束本次循環,而對用goto語句和if語句構成的循環,不能用break語句和continue語句進行控制。

練習:
有1、2、3、4個數字,能組成多少個互不相同且無重複數字的三位數?都是多少?
實現原理:

可填在百位、十位、個位的數字都是1、2、3、4,組成所有的排列後再去掉不滿足條件的排列。

代碼如下:

#include <stdio.h>

int main(){
	int i, j, k;
	for(i = 1; i <= 4; i++){
		for(j = 1; j <= 4; j++){
			for(k = 1; k <= 4; k++){
				if(i != j && i != k && j != k){     // 確保i、j、k三位互不相同
					printf("%d%d%d\n", i, j, k);
				}
			}
		}
	}

	return 0;
}

打印:

123
124
132
134
142
143
213
214
231
234
241
243
312
314
321
324
341
342
412
413
421
423
431
432

七、break和continue語句

1.break語句

break語句用來從循環體內跳出循環體,即提前結束循環,接着執行循環下面的語句。
一般形式:

break;

break語句不能用於循環語句和switch語句之外的任何其他語句中。
注意:

  • break語句對if-else的條件語句不起作用。
  • 在多層循環中,一個break語句只向外跳一層。

練習如下:

#include <stdio.h>

int main(){
	double pi = 3.14159, area;
	int r;
	for(r = 1; r <= 10; r++){
		area = pi * r * r;
		if(area > 100){
			break;
		}
		printf("r=%d, area=%g\n", r, area);
	}

	return 0;
}

打印:

r=1, area=3.14159
r=2, area=12.5664
r=3, area=28.2743
r=4, area=50.2654
r=5, area=78.5397

顯然,雖然理論上可以循環10次,但是由於r=6時面積已經大於100,所以執行break語句,提前結束循環,即不再繼續執行其餘的幾次循環。

再來一個稍複雜的練習:

#include <stdio.h>
#include <conio.h>

int main(){
	int i = 0;
	char c;
	while(1){
		c = '\0';
		while(c!=13 && c!=27){      // 鍵盤接收字符直到按回車或Esc鍵
			c = getch();
			printf("%c\n", c);
		}
		if(c==27){      // 判斷若按Esc鍵則退出循環
			break;
		}
		i++;
		printf("The number is %d\n", i);
	}
	printf("End");

	return 0;
}

打印:

a
s
d
f
g

The number is 1
q
w
e
r
t

The number is 2
1
2
3
4
5

The number is 3

End

顯然,內層循環按回車或Esc退出,外層循環按Esc退出。
其中getch()適用於從控制檯讀取一個字符、但不顯示在屏幕上,需要引入頭文件conio.h

2.continue語句

作用爲結束本次循環,即跳過循環體中下面尚未執行的語句,接着進行下一次是否執行循環的判定。
一般形式:

continue;

練習如下:

#include <stdio.h>

int main(){
	double pi = 3.14159, area;
	int r;
	for(r = 1; r <= 10; r++){
		area = pi * r * r;
		if(area > 100 && area < 120){
			continue;
		}
		printf("r=%d, area=%g\n", r, area);
	}

	return 0;
}

打印:

r=1, area=3.14159
r=2, area=12.5664
r=3, area=28.2743
r=4, area=50.2654
r=5, area=78.5397
r=7, area=153.938
r=8, area=201.062
r=9, area=254.469
r=10, area=314.159

顯然,此時只是跳過了第6次循環,後邊的迭代繼續進行。

3.continue和break的對比

continue語句只結束本次循環,而不是終止整個循環的執行;
break語句則是結束整個循環過程,不再判斷執行循環的條件是否成立。

練習:
顯示輸入的字符,如果按的是Esc鍵,則退出循環;如果按的是Enter鍵,則不做任何處理,繼續輸入下一個字符。
代碼如下:

#include <conio.h>

int main(){
	char c;
	for( ; ; ){
		c = getch();        // 字符輸入函數
		if(c==27){
			break;
		}
		if(c==13){
			continue;
		}
		putch(c);       // 顯示輸入的字符
	}
	getch();    // 讓程序停一下,按任意鍵繼續

	return 0;
}

打印:

abcdef123

此時輸入普通字符會顯示,鍵入回車會跳過、不換行,鍵入Esc會跳出循環,再輸入即退出。
其中,getch()putch()需要引入conio.h頭文件。

練習:
把100~200之間的不能被3整除的數輸出。
實現原理:
當n能被3整除時,執行continue語句,結束本次循環(即跳過printf函數語句),只有n不能被3整除時才執行printf函數。
代碼如下:

#include <stdio.h>

int main(){
	int i;
	for(i = 100; i <= 200; i++){
		if(i % 3 == 0){
			continue;
		}
		printf("%d\n", i);
	}

	return 0;
}

打印:

100
101
103
104
106
107
109
110
112
113
115
116
118
119
121
122
124
125
127
128
130
131
133
134
136
137
139
140
142
143
145
146
148
149
151
152
154
155
157
158
160
161
163
164
166
167
169
170
172
173
175
176
178
179
181
182
184
185
187
188
190
191
193
194
196
197
199
200

練習:
π/4 ≈ 1-1/3+1/5-1/7+…公式求π的近似值,直到某一項的絕對值小於爲止。
實現原理:
要確定計算的精度,可以配合while循環語句用fabs()函數確定精度來退出;
據觀察,分子不變,分母卻每次遞增2,且正負切換;
結果乘以4。

代碼如下:

#include <stdio.h>
#include <math.h>

int main(){
	int i = 1;
	double s = 1.0;
	double pt = 0, pi;
	while(fabs(s/i) > 1e-8){
		pt += s / i;
		s = -s;
		i += 2;
	}
	pi = pt * 4;
	printf("PI=%10.8f", pi);

	return 0;
}

打印:

PI=3.14159263

顯然,得到了π的估計值。

練習:
求Fibonacci數列前40個數。
這個數列有如下特點:第1,2兩個數爲1,1;從第3個數開始,該數是其前面兩個數之和。

F(1) = 1 (n=1)
F(2) = 1 (n=2)
F(n) = F(n-1)+F(n-2) (n≥3)

代碼如下:

#include <stdio.h>

int main(){
	long i, f1 = 1, f2 = 1;
	for(i = 1; i <= 20; i++){
		printf("%10ld %10ld ", f1, f2);
		f1 += f2;
		f2 += f1;
		if(i % 2 == 0){
			printf("\n");
		}
	}	
	
	return 0;
}

打印:

         1          1          2          3
         5          8         13         21
        34         55         89        144
       233        377        610        987
      1597       2584       4181       6765
     10946      17711      28657      46368
     75025     121393     196418     317811
    514229     832040    1346269    2178309
   3524578    5702887    9227465   14930352
  24157817   39088169   63245986  102334155

練習:
寫一個程序,允許輸入一個數,並判斷這個數是否爲素數。
其中,素數又稱質數,指在一個大於1的自然數中,除了1和此整數自身外,沒法被其他自然數整除的數。
換句話說,只有兩個正因數(1和自己)的自然數即爲素數。

代碼如下:

#include <stdio.h>
#include <math.h>

int main(){
	int n, i, prime = 1;
	printf("Input a number:\n");
	scanf("%d", &n);
	for(i = 2; i <= sqrt(n); i++){
		if(n % i == 0){
			prime = 0;
			break;
		}
	}
	if(prime){
		printf("%d is a prime number\n", n);
	}
	else{
		printf("%d is not a prime number\n", n);
	}
	
	return 0;
}

打印:

Input a number:
23
23 is a prime number

其中,sqrt()函數用來求一個數的算術平方根,返回double型的值。

練習:
求100~200間的全部素數並打印出來。
代碼如下:

#include <stdio.h>
#include <math.h>

int main(){
	int n, i, c = 1;
	for(n = 100; n <= 200; n++){
		int prime = 1;
		for(i = 2; i <= sqrt(n); i++){
			if(n % i == 0){
				prime = 0;
				break;
			}
		}
		if(prime){
			printf("% 4d", n);
			if(c % 5 == 0){
				printf("\n");
			}
			c++;
		}
	}

	return 0;
}

打印:

 101 103 107 109 113
 127 131 137 139 149
 151 157 163 167 173
 179 181 191 193 197
 199

練習:
爲使電文保密,往往按一定規律將其轉換成密碼,收報人再按約定的規律將其譯回原文。
例如,可以按以下規律將電文變成密碼:

將字母A變成字母E,a變成e,即變成其後的第4個字母,W變成A,X變成B,Y變成C,Z變成D。
如輸入China!得到Glmre!

代碼如下:

#include <stdio.h>

int main(){
	char c = getchar();
	while(c != '\n'){
		if(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'){
			if(c >= 'w' && c <= 'z' || c >= 'W' && c <= 'Z'){
				c = c - 22;
			}
			else{
				c = c + 4;
			}
		}
		putchar(c);
		c = getchar();
	}
	printf("\n");
	
	return 0;
}

打印:

zxcvb12345ASDFGqweRT
dbgzf12345EWHJKuaiVX

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章