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