C语言再学习 -- 循环语句
C語言有三種循環(huán)結(jié)構(gòu):while、for和do while。下面依次講解
一、while循環(huán)
1、while介紹
while(邏輯表達(dá)式) //布爾值為1 真
{
反復(fù)執(zhí)行的語句
}? ? ? ? ? ? ? ? ? ? ? ? ? ??
只要邏輯表達(dá)式結(jié)果為真就反復(fù)不停執(zhí)行大括號里的語句,直到邏輯表達(dá)式結(jié)果為假循環(huán)結(jié)束
只要把邏輯表達(dá)式寫成1則循環(huán)成為死循環(huán),while(1)
while循環(huán)里可以使用break和continue;
break 關(guān)鍵字很重要,表示終止本層循環(huán)。現(xiàn)在這個例子只有一層循環(huán),當(dāng)代碼執(zhí)行到break 時,循環(huán)便終止。
如果把 break 換成 continue 會是什么樣子呢? continue 表示終止本次(本輪) 循環(huán)。當(dāng)代碼執(zhí)行到 continue 時,本輪循環(huán)終止,進(jìn)入下一輪循環(huán)。while( 1)也有寫成 while(true) 或者 while(1==1) 或者 while((bool) 1)等形式的,效果一樣。
還需注意:任何非零值都是真.比如,while (-1) == while (true)
2、while復(fù)合語句
復(fù)合語句是使用花括號組織起來的兩個或更過的語句,它也被稱為一個代碼塊。
例如:
/*程序1*/
index = 0; while (index++ < 10)sam = 10 *index +2; printf ("sam = %d\n", sam); /*程序2*/
index = 0; while (index++ < 10) {sam = 10 *index +2;printf ("sam = %d\n", sam); } 說明:在沒有花括號的情況下,while循環(huán)語句的范圍是從while到下一個分號。
3、技巧
A、假如你想要跳過輸入直到第一個不為空格或者數(shù)字的字符,你可以使用這樣的循環(huán):
while (scanf ("%d", &num) == 1) {....}
只要scanf()輸入一個整數(shù),它就返回1,循環(huán)就會繼續(xù)。
B、while ((ch = getchar ()) != '\n') {....}
只要輸入不為換行符'\n',它就返回1,循環(huán)就會繼續(xù)。
C、while循環(huán)內(nèi)只執(zhí)行一次的語句
int i = 0;
while (1)?
{
if (0 == i)
{只執(zhí)行一次該語句};
i == 1;
}
4、空語句
while ((ch = getchar ()) != EOF && ch != '\n') ; while語句之后的單獨一個分號稱為空語句,它就是應(yīng)用于目前這個場合,也就是語法要求這個地方出現(xiàn)一條語句但又無需執(zhí)行任何任務(wù)的時候,這個分號獨占一行,這是為了防止讀者錯誤地以為接下來的語句也是循環(huán)的一部分。
二、do while循環(huán)
1、do while介紹
do{
} ?while(為真的邏輯);
do while為退出條件循環(huán),判斷條件在執(zhí)行循環(huán)之后進(jìn)行檢查,這樣就可以保證循環(huán)體中的語句至少被執(zhí)行一次。
2、do while復(fù)合語句
例如:
do scanf ("%d", &num); while (num != 20); /*注意while后面有分號 ;*/
3、do while的常規(guī)用法
[cpp]?view plaincopy
4、do {}while(0)的用法
參看:do while的使用 linux內(nèi)核中 do{}while(0)用法分析linux內(nèi)核和其他一些開源的代碼中,經(jīng)常會遇到這樣的代碼:
| do{... }while(0) |
這樣的代碼一看就不是一個循環(huán),do..while表面上在這里一點意義都沒有,那么為什么要這么用呢?
實際上,do{...}while(0)的作用遠(yuǎn)大于美化你的代碼,while(0)的好處在于,在編譯的時候,編譯器基本都會為你做好優(yōu)化,把這部分內(nèi)容去掉,最終編譯的結(jié)果不會因為這個do while而導(dǎo)致運行效率上的差異。。查了些資料,總結(jié)起來這樣寫主要有以下幾點好處:
1、輔助定義復(fù)雜的宏,避免引用的時候出錯:
舉例來說,假設(shè)你需要定義這樣一個宏:
| #define DO_SOMETHING()\foo1();\foo2(); |
這個宏的本意是,當(dāng)調(diào)用DO_SOMETHING()時,函數(shù)foo1()和foo2()都會被調(diào)用。但是如果你在調(diào)用的時候這寫:
| if(a>0)DO_SOMETHING(); |
因為宏在預(yù)處理的時候會直接被展開,你實際上寫的代碼是這個樣子的:
| if(a>0)foo1(); foo2(); |
這就出現(xiàn)了問題,因為無論a是否大于0,foo2()都會被執(zhí)行,導(dǎo)致程序出錯。
那么僅僅使用{}將foo1()和foo2()包起來行么?
我們在寫代碼的時候都習(xí)慣在語句右面加上分號,如果在宏中使用{},代碼里就相當(dāng)于這樣寫了:“{...};”,展開后就是這個樣子:
| if(a>0) {foo1();foo2(); }; |
注意if{};后面的";",如果if后面還有else等語句則編譯不會通過。所以,很多人才采用了do{...}while(0);
| #define DO_SOMETHING() \do{ \foo1();\foo2();\}while(0)\ ...if(a>0)DO_SOMETHING();... |
這樣,宏被展開后,才會保留初始的語義。GCC提供了Statement-Expressions用以替代do{...}while(0); 所以你也可以這樣定義宏:
| #define DO_SOMETHING() ({\foo1(); \foo2(); \ }) |
http://www.spongeliu.com/?
2、避免使用goto對程序流進(jìn)行統(tǒng)一的控制:
有些函數(shù)中,在函數(shù)return之前我們經(jīng)常會進(jìn)行一些收尾的工作,比如free掉一塊函數(shù)開始malloc的內(nèi)存,goto一直都是一個比較簡便的方法:
| int foo() {somestruct* ptr = malloc(...);dosomething...;if(error){goto END;}dosomething...;if(error){goto END;}dosomething...;END:free(ptr);return 0;} |
由于goto不符合軟件工程的結(jié)構(gòu)化,而且有可能使得代碼難懂,所以很多人都不倡導(dǎo)使用,那這個時候就可以用do{}while(0)來進(jìn)行統(tǒng)一的管理:
| int foo() {somestruct* ptr = malloc(...);do{dosomething...;if(error){break;}dosomething...;if(error){break;}dosomething...;}while(0);free(ptr);return 0;} |
這里將函數(shù)主體使用do()while(0)包含起來,使用break來代替goto,后續(xù)的處理工作在while之后,就能夠達(dá)到同樣的效果。
3、避免空宏引起的warning
內(nèi)核中由于不同架構(gòu)的限制,很多時候會用到空宏,在編譯的時候,空宏會給出warning,為了避免這樣的warning,就可以使用do{}while(0)來定義空宏:
| #define EMPTYMICRO do{}while(0) |
4、定義一個單獨的函數(shù)塊來實現(xiàn)復(fù)雜的操作:
當(dāng)你的功能很復(fù)雜,變量很多你又不愿意增加一個函數(shù)的時候,使用do{}while(0);,將你的代碼寫在里面,里面可以定義變量而不用考慮變量名會同函數(shù)之前或者之后的重復(fù)。
5、do {}while(0)的簡化
#define st(x) do { x } while (__LINE__ == -1)
1, __LINE__ 是個宏,它代表當(dāng)前代碼在源文件的行號,它是大于0的,所以__LINE__ == -1 等同于0,化簡為:#define st(x) do { x } while (0)
2,do {} while (0)通常用于宏中, 為的是避免如下情況:
#define st(x) x
那么我們在調(diào)用 if (0) st(a = b; b = c;) 時會被解釋成
if(0)
a = b;
b = c;
可見 if 只對a = b;起作用。
三、for循環(huán) 1、for介紹 for(num=1; num <10; num++); 在關(guān)鍵字for之后的圓括號中包含了由兩個分號分開的三個表達(dá)式。 第一個表達(dá)式進(jìn)行初始化,它在for循環(huán)開始的時候執(zhí)行一次。 第二個表達(dá)式是判斷條件,在每次執(zhí)行循環(huán)之前都要對它進(jìn)行求值。當(dāng)表達(dá)式為假時,循環(huán)結(jié)束。 第三個表達(dá)式進(jìn)行改變或者稱為更新,它在每次循環(huán)結(jié)束時進(jìn)行計算。
2、for復(fù)合語句 例如: for (n = 0; n < 10; n+)printf ("%d %d\n", n, 2 * n + 1);
3、for循環(huán)的靈活性 A、可以讓一個或多個表達(dá)式為空(但是不要遺漏分號)。只須確保在循環(huán)中包含一些能是循環(huán)最終結(jié)束的語句。
B、順便說一句,中間的那個控制表達(dá)式為空會被認(rèn)為是真,所以下面的循環(huán)會永遠(yuǎn)執(zhí)行: for (; ;) printf ("hello world\n");
C、第一個表達(dá)式不必初始化一個變量,它也可是某種類型的printf()語句,要記住第一個表達(dá)式只在執(zhí)行循環(huán)的其他部分之前被求值或執(zhí)行一次。
D、for循環(huán)可使用逗號運算符把兩個表達(dá)式鏈接為一個表達(dá)式,并保證最左邊的表達(dá)式最先計算。 例如: for (n = 2, m = 0; m < 1000; n *=2)m +=n;
E、在for循環(huán)中使用數(shù)組,使用#define來指定數(shù)組大小 例如: #define SIZE 10 for (int n = 0; n < SIZE; n++)printf ("hello world\n");
4、嵌套循環(huán) A、嵌套循環(huán)是指在另一個循環(huán)之內(nèi)的循環(huán)。通常使用嵌套循環(huán)來按行按列顯示數(shù)據(jù)。也就是說一個循環(huán)處理一行中的所有列,而另一個循環(huán)處理所有的行。
B、外層for循環(huán)為外部循環(huán),按行顯示數(shù)據(jù);內(nèi)層for循環(huán)為內(nèi)部循環(huán)。按列顯示數(shù)據(jù)。 在多重循環(huán)中,如果有可能,應(yīng)當(dāng)將最長的循環(huán)放在最內(nèi)層,最短的循環(huán)放在最外層,以減少 CPU 跨切循環(huán)層的次數(shù)。例如:
長循環(huán)在最內(nèi)層,效率高 for (col=0; col<5; col++ ) {for (row=0; row<100; row++){sum = sum + a[row][col];} }長循環(huán)在最外層,效率低 for (row=0; row<100; row++) {for ( col=0; col<5; col++ ){sum = sum + a[row][col];} }
C、建議 for 語句的循環(huán)控制變量的取值采用“半開半閉區(qū)間”寫法。半開半閉區(qū)間寫法和閉區(qū)間寫法雖然功能是相同,但相比之下,半開半閉區(qū)間寫法寫法更加直觀。 半開半閉區(qū)間寫法:for (n = 0; n < 10; n++) {....} 閉區(qū)間寫法:for (n = 0; n <= 9; n++) {....}
D、不能在 for 循環(huán)體內(nèi)修改循環(huán)變量,防止循環(huán)失控。 for (n = 0; n < 10; n++) {…n = 8;//不可,很可能違背了你的原意… }
E、循環(huán)要盡可能的短,要使代碼清晰,一目了然。 如果你寫的一個循環(huán)的代碼超過一顯示屏,那會讓讀代碼的人發(fā)狂的。解決的辦法由兩個:第一,重新設(shè)計這個循環(huán),確認(rèn)是否這些操作都必須放在這個循環(huán)里;第二,將這些代碼改寫成一個子函數(shù), 循環(huán)中只調(diào)用這個子函數(shù)即可。 一般來說循環(huán)內(nèi)的代碼不要超過 20行。
F、把循環(huán)嵌套控制在 3 層以內(nèi)。 國外有研究數(shù)據(jù)表明,當(dāng)循環(huán)嵌套超過 3 層,程序員對循環(huán)的理解能力會極大的降低。如果你的循環(huán)嵌套超過 3 層,建議你重新設(shè)計循環(huán)或是將循環(huán)內(nèi)的代碼改寫成一個字函數(shù)。
G、擴展 1、編程實現(xiàn)打印一個空心的菱形和實心的菱形:
? ? ? ? ?*
? ? ? *****
? ?*********
*************
? ?*********
? ? ? *****
? ? ? ? ?*
#include <stdio.h> int main() {int i=0,j=0,k=0;for(i=0;i<=3;i++){for(j=0;j<=5-2*i;j++){printf(" ");}for(k=0;k<=4*i;k++){printf("*");}printf("\n");}for(i=0;i<=2;i++){for(j=0;j<=2*i+1;j++){printf(" ");}for(k=0;k<=8-4*i;k++){printf("*");}printf("\n");}return 0; }
2、請簡述以下兩個 for 循環(huán)的優(yōu)缺點 // 第一個 for (i=0; i<N; i++) { if (condition) DoSomething(); else DoOtherthing(); } 優(yōu)缺點:效率低但程序簡潔
// 第二個 if (condition) { for (i=0; i<N; i++) DoSomething(); } else { for (i=0; i<N; i++) DoOtherthing(); }優(yōu)缺點:效率高但程序不簡潔
說明:第一個程序比第二個程序多執(zhí)行了 N-1 次邏輯判斷。并且由于前者老要進(jìn)行邏輯判斷,打斷了循環(huán)“流水線”作業(yè),使得編譯器不能對循環(huán)進(jìn)行優(yōu)化處理,降低了效率。如果 N 非常大,最好采用第二個程序的寫法,可以提高效率。如果 N 非常小,兩者效率差別并不明顯,采用第一個程序的寫法比較好,因為程序更加簡潔。
注意: 1、上面有提到,在多重循環(huán)中,如果有可能,應(yīng)當(dāng)將最長的循環(huán)放在最內(nèi)層,最短的循環(huán)放在最外層,以減少 CPU 跨切循環(huán)層的次數(shù)。 ?
2、建議 for 語句的循環(huán)控制變量的取值采用“半開半閉區(qū)間”寫法。
int a[10]; 半開半閉區(qū)間寫法:for (n = 0; n < 10; n++)
閉區(qū)間寫法:for (n = 0; n <= 9; n++)
半開半閉區(qū)間寫法和閉區(qū)間寫法雖然功能是相同,但考慮到邊界計算,例如我們要在C語言中定義一個擁有 10 個元素的數(shù)組,那么 0 就是數(shù)組下標(biāo)的第一個 “入界點”(指處于數(shù)組下標(biāo)范圍以內(nèi)的點,包括邊界點),而 10 就是數(shù)組下標(biāo)中的第一個“出界點”(指不在數(shù)組下標(biāo)范圍以內(nèi)的點,不含邊界點)。因此寫成半開半閉區(qū)間的寫法更加直觀。
3、不能在 for 循環(huán)體內(nèi)修改循環(huán)變量,防止循環(huán)失控。for (n = 0; n < 10; n++) { … n = 8;//不可,很可能違背了你的原意 … }
4、循環(huán)要盡可能的短,要使代碼清晰,一目了然。
如果你寫的一個循環(huán)的代碼超過一顯示屏,那會讓讀代碼的人發(fā)狂的。解決的辦法由兩個:第一,重新設(shè)計這個循環(huán),確認(rèn)是否這些操作都必須放在這個循環(huán)里;第二,將這些代碼改寫成一個子函數(shù), 循環(huán)中只調(diào)用這個子函數(shù)即可。 一般來說循環(huán)內(nèi)的代碼不要超過 20行。
5、把循環(huán)嵌套控制在 3 層以內(nèi)。
國外有研究數(shù)據(jù)表明,當(dāng)循環(huán)嵌套超過 3 層,程序員對循環(huán)的理解能力會極大的降低。如果你的循環(huán)嵌套超過 3 層,建議你重新設(shè)計循環(huán)或是將循環(huán)內(nèi)的代碼改寫成一個字函數(shù)。
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖
總結(jié)
以上是生活随笔為你收集整理的C语言再学习 -- 循环语句的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux 中/etc/profile、
- 下一篇: 结构体变量和结构体指针变量作为函数参数传