第 10 講:跳轉(zhuǎn)結(jié)構(gòu)
還記得前文學(xué)到的質(zhì)數(shù)判斷和嵌套循環(huán)嗎?前文遇到了一個(gè)例子,我們嘗試跳出循環(huán),但發(fā)現(xiàn)知識(shí)不足以讓我們?cè)谄陂g跳出循環(huán)的執(zhí)行,所以我們?cè)谀莻€(gè)時(shí)候無能為力,好在這個(gè)思路并不會(huì)使得代碼的邏輯變成錯(cuò)的,它僅僅涉及到代碼執(zhí)行的優(yōu)化。
今天的內(nèi)容,我們將會(huì)處理這些特殊的處理。我們一共要學(xué)習(xí)三種語句模式:“break
語句”、“continue
語句”和“標(biāo)簽 + goto
語句”模型。
break
語句:跳出循環(huán)
我們省略了一些代碼,只關(guān)注于循環(huán)語句,和 break
語句。break
語句是單獨(dú)有分號(hào)結(jié)尾的,否則它將只是一個(gè)關(guān)鍵字。
在執(zhí)行循環(huán)的時(shí)候,如果發(fā)現(xiàn)它已經(jīng)不是質(zhì)數(shù)了,就沒必要再往下執(zhí)行后面的判斷是否除盡的邏輯了,于是在這里就跳出循環(huán),所以寫在應(yīng)當(dāng)跳出循環(huán)的位置上。
很多小伙伴在初學(xué)這個(gè)語句的時(shí)候,以為它可以跳出
if
條件。請(qǐng)注意,break
語句是只跟包含它的這層循環(huán)有關(guān),它僅能跳出一層循環(huán)。也就是說,如果有兩個(gè)while
循環(huán),如果把break
語句寫在最里面,那么它也只能跳出最內(nèi)層的循環(huán),而靠外的這一層循環(huán)依舊會(huì)繼續(xù)往下執(zhí)行,比如原本的例子:
這個(gè)
break
語句只能讓語句跳轉(zhuǎn)到第 14 行繼續(xù)執(zhí)行,所以下方的isPrime
的判斷條件它照樣要走。所以這么考慮起來,這個(gè)break
語句的跳轉(zhuǎn)邏輯是沒有問題的。
continue
語句:執(zhí)行下一次循環(huán)
要說 break
語句是跳出循環(huán),那么和它寫法類似的 continue
語句就不是這個(gè)行為了。continue
語句直接終結(jié)當(dāng)前這一次循環(huán)的執(zhí)行邏輯,直接跳轉(zhuǎn)到循環(huán)的開頭去判斷條件(而 for
循環(huán)則跳轉(zhuǎn)到增量處)。
上述示例給出了一個(gè)從 1 到 10 的循環(huán)。當(dāng)循環(huán)執(zhí)行的過程中發(fā)現(xiàn) n 本身被 3 整除,就會(huì)自動(dòng)跳轉(zhuǎn)到 n++
增量語句處執(zhí)行,而不會(huì)遇到 printf("%d ", n);
語句。這個(gè)示例展示了如何輸出不是 3 的整倍數(shù)的代碼。
它和 break
語句不一樣的地方在于,continue
不會(huì)退出代碼,而是跳轉(zhuǎn)到增量(for
循環(huán))或條件(while
或 do-while
循環(huán))處執(zhí)行。
goto
語句:跳轉(zhuǎn)到任意的執(zhí)行位置
用 goto
代替循環(huán)
最為靈活的語句非 goto
語句莫屬,它可以跳轉(zhuǎn)到程序的任意你想要到達(dá)的位置上去(當(dāng)然,在 main
函數(shù)外面去是不可以的)。
考慮如下行為。如果我們沒有循環(huán)的時(shí)候,我們可以嘗試使用 goto
來達(dá)到循環(huán)的目的。
上述示例將會(huì)輸出從 1 到 100 內(nèi)的數(shù)字的和。當(dāng)執(zhí)行到條件滿足時(shí),將會(huì)自動(dòng)統(tǒng)計(jì)結(jié)果,并跳轉(zhuǎn)到 Loop
標(biāo)簽處(第 3 行)繼續(xù)執(zhí)行代碼。直到條件不成立的時(shí)候,到 else
部分,輸出結(jié)果,此時(shí)沒有 goto
語句,所以會(huì)繼續(xù)往下執(zhí)行,而不會(huì)再返回上面了。
請(qǐng)注意,標(biāo)簽名稱的定義依然滿足標(biāo)識(shí)符的命名規(guī)則(數(shù)字、字母、下劃線的那些規(guī)矩),所以不要亂取名。而且,標(biāo)簽不能位于函數(shù)的外面。比如下面的這個(gè)示例,就會(huì)出現(xiàn)錯(cuò)誤:
這段代碼是會(huì)報(bào)錯(cuò)的,不是運(yùn)行的時(shí)候出錯(cuò),而是直接在運(yùn)行和編譯程序之前就會(huì)出現(xiàn)錯(cuò)誤。
你叫它標(biāo)簽?
是的,在前文里,使用標(biāo)識(shí)符規(guī)則加一個(gè)冒號(hào)的部分我們稱為標(biāo)簽(Label)。標(biāo)簽并非任何執(zhí)行語句,它沒有意義,跟注釋差不多。但和注釋的區(qū)別在于,注釋在運(yùn)行的 exe 里不會(huì)出現(xiàn),而標(biāo)簽?zāi)憧赡苓€會(huì)查到這個(gè)東西存在的影子。
標(biāo)簽可以控制程序運(yùn)行的順序和行為,比如上面正確的那一則例子,它可以控制語句形成一個(gè)循環(huán),來達(dá)到模擬循環(huán)的效果。而且,在前文介紹的 switch
語句也大量使用到了標(biāo)簽。仔細(xì)回憶一下,switch
語句配合 case
語句使用,而 case
語句末尾就是用的冒號(hào)。是的,case
語句就是一個(gè)特殊的標(biāo)簽,它用于規(guī)定 switch
小括號(hào)里的表達(dá)式的結(jié)果的分情況處理行為。當(dāng)都不成立的時(shí)候,語句將會(huì)跳轉(zhuǎn)到 default
標(biāo)簽處執(zhí)行。
但可以發(fā)現(xiàn),它并不影響程序的真正運(yùn)行規(guī)范(比如大括號(hào)并不會(huì)因?yàn)闃?biāo)簽的行為而斷開和分割開),所以請(qǐng)注意,像是 switch
語句或是上面的這樣的例子里,我們不能在同一個(gè)范圍里定義兩個(gè)名字完全一樣的變量。比如說
或是
兩個(gè)例子里,變量在錯(cuò)誤的代碼處是完全可以看到第一行給出的變量的,所以在可以看到的地方重新定義這些變量是有語義沖突的。
不過,C 語言允許第 2 種情況的代碼,意味著這個(gè)代碼不會(huì)出錯(cuò),但錯(cuò)誤很隱蔽:在
case 3
和case 5
標(biāo)簽處各定義了一個(gè)i
變量,而這兩處的i
和第 1 行定義的i
并不是同一個(gè)變量,而是兩個(gè)完全沒有關(guān)系的變量,只是恰好這里同名了。我們應(yīng)盡量避免這種定義行為。