嵌入式C中的goto語句,爭議很大
什么是goto語句?
goto
語句被稱為 C 語言中的跳轉(zhuǎn)語句,用于無條件跳轉(zhuǎn)到其他標(biāo)簽。它將控制權(quán)轉(zhuǎn)移到程序的其他部分。
goto
語句一般很少使用,因為它使程序的可讀性和復(fù)雜性變得更差。
語法
goto label;
goto 語句示例
讓我們來看一個簡單的例子,演示如何使用 C 語言中的 goto 語句。
打開 Visual Studio 創(chuàng)建一個名稱為:goto 的工程,并在這個工程中創(chuàng)建一個源文件:goto-statment.c
,其代碼如下所示:
#include void main(){ int age;gotolabel: printf("You are not eligible to vote!n"); printf("Enter you age:n"); scanf("%d", &age); if (age < 18) { goto gotolabel; } else { printf("You are eligible to vote!n"); }}
執(zhí)行上面代碼,得到以下結(jié)果
You are not eligible to vote!Enter you age:12You are not eligible to vote!Enter you age:18You are eligible to vote!
為什么它這么不受待見?
二十幾年前,當(dāng)計算機編程尚處于起步階段時,程序流程是由 “GOTO” 語句來控制。該類語句允許程序員對當(dāng)前代碼行斷行,而直接進入另一個不同的代碼段。
下圖為簡單的示例。
編程語言終究開始引入了函數(shù)的概念,即允許程序?qū)Υa進行斷行。如果已經(jīng)完成,不再使用 goto 語句來表示代碼的斷行。
函數(shù)調(diào)用后,函數(shù)將回到下一條指令。下圖為示例。
這一做法改善了程序結(jié)構(gòu),提高了可讀性。自此,這被視為編寫程序的正確方法。只要看到或想到 goto 語句,就會讓軟件工程師退縮,產(chǎn)生本能的厭惡。
在 wikipedia 上的解釋就是:
GOTO語句一直是批評和爭論的目標(biāo),主要的負面影響是使用GOTO語句使程序的可讀性變差,甚至成為不可維護的「面條代碼」。
隨著結(jié)構(gòu)化編程在二十世紀六十年代到七十年代變得越來越流行,許多計算機科學(xué)家得出結(jié)論,即程序應(yīng)當(dāng)總是使用被稱為「結(jié)構(gòu)化」控制流程的命令,以及 if-then-else
語句來替代 GOTO。甚至在今天,許多程序風(fēng)格編碼標(biāo)準禁止使用 GOTO 語句。
也有不少人為 GOTO 語句辯護,他們認為只要加以限制地使用 GOTO 語句不會導(dǎo)致低質(zhì)量的代碼,并且在許多編程語言中,一些功能難以在不使用 GOTO 語句的情況下實現(xiàn)。比如有限狀態(tài)機的實現(xiàn)、跳出嵌套循環(huán)以及異常處理等等。
大概最著名的對于 GOTO 的批評是艾茲格·迪杰斯特拉(Edsger Wybe Dijkstra)在1968年的一篇名為《GOTO陳述有害輪》的論文。
迪杰斯特拉認為不加限制地使用GOTO語句應(yīng)當(dāng)從高級語言中廢止,因為它使分析和驗證程序正確性(特別是涉及循環(huán))的任務(wù)變得復(fù)雜。
另外一種觀點出現(xiàn)在高德納的Structured Programming with go to Statements
中,文章分析了許多常見編程任務(wù),然后發(fā)現(xiàn)其中的一些使用GOTO將得到最理想的結(jié)構(gòu)。
限制GOTO
許多語言,如 C 語言和 Java,提供了相關(guān)的控制流語句,如 break
和 continue
,它們都是有效地被限制的 goto 語句。它們的作用是無條件跳轉(zhuǎn),但是只能夠跳到循環(huán)塊結(jié)束的位置 —— 繼續(xù)進入下一循環(huán)(continue)或者結(jié)束循環(huán)(break)。
switch/case結(jié)構(gòu)
C 語言、C++ 和 Java 中的 switch 語句高效地實現(xiàn)了一個多路 goto,跳轉(zhuǎn)目標(biāo)由表達式的值來選擇。這也導(dǎo)致了我們沒有不得不使用 goto 的理由。
針對這些,導(dǎo)致目前 goto 的使用情況是這樣的:goto 語句的結(jié)果在C/C++等高級編程語言中保留了goto語句,但被建議不用或少用。
在一些更新的高級編程語言,如 Java 不提供 goto 語句,它雖然指定 goto 作為關(guān)鍵字,但不支持它的使 用,使程序簡潔易讀;盡管如此后來的 c# 還是支持 goto 語句的,goto 語句一個好處就是可以保證程序存在唯一的出口,避免了過于龐大的 if 嵌套。
另一方面,goto 語句只是不提倡,當(dāng)然不是禁用,那么在什么情況下可以使用 goto 語句呢?
可以考慮使用 goto 的情形:
· 從多重循環(huán)中直接跳出 ;
· 出錯時清除資源;
· 可增加程序的清晰度的情況。
不加限制地使用 goto:破壞了清晰的程序結(jié)構(gòu),使程序的可讀性變差,甚至成為不可維護的"面條代碼"。
經(jīng)常帶來錯誤或隱患,比如它可能跳過了某些對象的構(gòu)造、變量的初始化、重要的計算等語句。
下列關(guān)于使用 goto 語句的原則可以供讀者參考。
1. 使用 goto 語句只能 goto 到同一函數(shù)內(nèi),而不能從一個函數(shù)里 goto 到另外一個函數(shù)里。
2. 使用 goto 語句在同一函數(shù)內(nèi)進行 goto 時,goto 的起點應(yīng)是函數(shù)內(nèi)一段小功能的結(jié)束處,goto 的目的 label 處應(yīng)是函數(shù)內(nèi)另外一段小功能的開始處。
3. 不能從一段復(fù)雜的執(zhí)行狀態(tài)中的位置 goto 到另外一個位置,比如,從多重嵌套的循環(huán)判斷中跳出去就是不允許的。
4. 應(yīng)該避免像兩個方向跳轉(zhuǎn)。這樣最容易導(dǎo)致"面條代碼"。
閱讀過 linux 內(nèi)核代碼的同學(xué)應(yīng)該注意到,linux 內(nèi)核代碼里面其實有不少地方用了 goto 語句,
這是在/drivers/i2c/i2c-dev.c
中的i2c_dev_init
函數(shù):
static int __init i2c_dev_init(void){ int res; pr_info("i2c /dev entries drivern"); res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c"); if (res) goto out; i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); if (IS_ERR(i2c_dev_class)) { res = PTR_ERR(i2c_dev_class); goto out_unreg_chrdev; } i2c_dev_class->dev_groups = i2c_groups; /* Keep track of adapters which will be added or removed later */ res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier); if (res) goto out_unreg_class; /* Bind to already existing adapters right away */ i2c_for_each_dev(NULL, i2cdev_attach_adapter); return 0;out_unreg_class: class_destroy(i2c_dev_class);out_unreg_chrdev: unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);out: pr_err("Driver Initialisation failedn"); return res;}
但是你會發(fā)現(xiàn),這些地方的goto
語句,使用非常謹慎,基本都遵循上面提到的幾個原則。
本文來源網(wǎng)絡(luò),免費傳達知識,版權(quán)歸原作者所有。如涉及作品版權(quán)問題,請聯(lián)系我進行刪除。
評論