goto語句

goto語句

goto語句也稱為無條件轉移語句,其一般格式如下: goto 語句標號;其中語句標號是按標識符規定書寫的符號,放在某一語句的前面,標號后加冒號(:)。語句標號起標識語句的作用,與goto 語句配合使用。

C語言


統計從鍵盤輸入一行字元的個數。
例如輸入:abcdefghijklmnopqrstuvwxyz
然後回車Enter
輸出:26
本例用if語句和goto語句構成循環結構。當輸入字元不為'\n'時即執行n++進行計數。
然後轉移至if語句循環執行,直至輸入字元為'\n'才停止循環。

彙編語言


goto語句與彙編語言裡面的jmp指令相同,(無條件轉移)
1+2+3........+8+9+10
NASM描述:
mov ax,1
mov bx,0
start:add bx,ax
inc ax
cmp ax,11;比較指令
jzend ;零轉移
jmp start
end:
;
;start: end:是標號

Pascal


格式 goto <標號>;
其中標號需要申請,在程序開頭寫label <標號1>,<標號2>,……;
其中,標號必須為四位以內的正整數。
在該段落內還需要有<標號>:語句 表示將要轉向的方向。

delphi


//Goto語句用在跳轉行號, 可以跳轉到當前結構層內任意位置.
//必須在聲明處用label關鍵字聲明行號.
//由於Goto語句會破壞程序的結構, 不推薦使用.
var
a,b: Integer;
label
X,Y;
begin
if a > b then
else
goto Y;
X:
WriteLn('a > b');
Y:
WriteLn('b > a');
end;

批處理


使用冒號標記
:start
goto start

按鍵精靈


使用rem做標記,可以用中文
rem 開始
goto 開始

VBA


使用冒號做標記,但是跟批處理的不一樣,冒號在後面
start:
goto start

發展歷程


問題起源:
60年代中期以後,計算機硬體技術日益進步,計算的存貯容量、運算速度和可靠性明顯提高,生產硬體的成本不斷降低。計算機價格的下跌為它的廣泛應用創造了極好的條件。在這種形勢下,迫切要求計算機軟體也能與之相適應。因而,一些開發大型軟體系統的要求提了出來。然而軟體技術的進步一直未能滿足形勢發展的需要,在大型軟體的開發過程中出現了複雜程度高、研製周期長、正確性難以保證的三大難題。遇到的問題找不到解決辦法,致使問題堆積起來,形成了人們難以控制的局面,出現了所謂的“軟體危機”。為了克服這一危機,一方面需要對程序設計方法、程序的正確性和軟體的可靠性等問題進行系列的研究;另一方面,也需要對軟體的編製、測試、維護和管理的方法進行研究,從而產生了程序設計方法學。
goto語句是有害的觀點:
1968年,Edsger Wybe Dijkstra 首先提出“GOTO語句是有害的”論點,向傳統程序設計方法提出了挑戰,從而引起了人們對程序設計方法討論的普遍重視。
goto語句的爭論:
在60年代末和70年代初,關於GOTO語句的用法的爭論比較激烈。主張從高級程序語言中去掉GOTO語句的人認為,GOTO語句是對程序結構影響最大的一種有害的語句,他們的主要理由是:GOTO語句使程序的靜態結構和動態結構不一致,從而使程序難以理解,難以查錯。去掉GOTO語句后,可直接從程序結構上反映程序運行的過程。這樣,不僅使程序結構清晰,便於理解,便於查錯,而且也有利於程序的正確性證明。
持反對意見的人認為,GOTO語句使用起來比較靈活,而且有些情形能提高程序的效率。若完全刪去GOTO語句,有些情形反而會使程序過於複雜,增加一些不必要的計算量。
關於goto語句的解決方法:
1974年,D·E·克努斯對於GOTO語句爭論作了全面公正的評述,其基本觀點是:不加限制地使用GOTO語句,特別是使用往回跳的GOTO語句,會使程序結構難於理解,在這種情形,應盡量避免使用GOTO語句。但在另外一些情況下,為了提高程序的效率,同時又不至於破壞程序的良好結構,有控制地使用一些GOTO語句也是必要的。用他的話來說就是:“在有些情形,我主張刪掉GOTO語句;在另外一些情形,則主張引進GOTO語句。”從此,使這場長達10年之久的爭論得以平息。
後來,G·加科皮尼和C·波姆從理論上證明了:任何程序都可以用順序、分支和重複結構表示出來。這個結論表明,從高級程序語言中去掉GOTO語句並不影響高級程序語言的編程能力,而且編寫的程序的結構更加清晰。
goto語句的結果:
在C/C++等高級編程語言中保留了goto語句,但被建議不用或少用。在一些更新的高級編程語言,如Java不提供goto語句,它雖然指定goto作為關鍵字,但不支持它的使 用,使程序簡潔易讀;儘管如此後來的c#還是支持goto語句的,goto語句一個好處就是可以保證程序存在唯一的出口,避免了過於龐大的if嵌套。
二。可以考慮使用goto的情形
1.從多重循環中直接跳出
很多人建議廢除C++/C的goto語句,以絕後患。但實事求是地說,錯誤是程序員自己造成的,不是goto的過錯。goto 語句至少有一處可顯神通,它能從多重循環體中一下子跳到外面,用不著寫很多次的break語句。例如:
for(......){
for(....){
for(.....){
// 如何衝出重重包圍?
}
}
}
break;只能跳出單層的循環,return將整個函數都返回了,沒法再繼續了,顯然也不行,所以我們想到了goto。如果是在陷入了很深層次的循環里想要跳出最外層的循環,用 goto 直接跳出去比用 break 一個循環一個循環地跳出要好得多。有人甚至形象比喻說:“就像樓房著火了,來不及從樓梯一級一級往下走,可從窗口跳出火坑。”其實,你可以將 break 和 continue 理解成弱化了的 goto 語句。
2. 出錯時清除資源
如果一個函數有多個出口,則在每個出口處,會產生巨大的退出代碼,如下例一,每個函數只能有一個出口,所有的資源釋放必須放在出口統一解決,那全部使用大括弧,十幾個,幾十個if判斷條件下來,你數數你的大括弧有多深?這種代碼可讀性不好,一旦寫錯了,難於尋找錯誤。所有這些問題,一個goto就解決了。
當程序要分配和清除資源時(像內存、或處理字形、窗口、印表機),這種情形下用goto通常是為了複製代碼或清除資源。若遇到這種情況,程序員就要掂量是 goto 的缺點令人討厭呢?還是複製代碼那令人頭痛的維護更討厭呢?最後還是認為 goto 的缺點更可忍受。
例子一:不用goto,想想需要申請的指針是10個的話,程序怎麼寫?
void Func(void)
{
char* p1=null;
if(!p1) return;
p2=(char*)malloc(10);
if(!p2)
{
free(p1);
p1=null;
return;
}
p3=(char*)malloc(10);
if(!p3)
{
free(p1);
p1=null;
free(p2);
p2=null;
return;
}
……
……
…… //指針使用過程
if(p1)
{
free(p1);
p1=null;
例子二:用goto
void Func(void)
{
char* p1=null;
char* p2=null;
char* p3=null;
p1=(char*)malloc(10);
if(!p1) goto Func_End_Process;
p2=(char*)malloc(10);
if(!p2) goto Func_End_Process;
p3=(char*)malloc(10);
if(!p3) goto Func_End_Process;
…… //指針使用過程
Func_End_Process:
if(p1)
{
free(p1);
p1=null;
}
if(p2)
{
free(p2);
p2=null;
}
if(p3)
{
free(p3);
p3=null;
}
}
3.可增加程序的清晰度的情況。
若不使用goto語句會使功能模糊,有時候使用goto語句,一眼就看清楚了程序的意圖,可用那些對應的循環break語句等實現的語句段,要想老半天才搞清楚程序意圖的情況,也可考慮使用goto語句。
三。不加限制地使用goto帶來的弊端
1).很明顯,不加限制地使用goto破壞了清晰的程序結構,使程序的可讀性變差,甚至成為不可維護的"麵條代碼"。例如下例:
[code=C/C++]
A: //code section A
//code
goto B;
//code
goto C;
B: //code section B
//code
goto A;
//code
goto C;
C: //code section C
//code
//goto B;
//code
goto A;
[/code]
這樣好像已經能夠說明問題了,隨著標籤的增多,帶來的混亂局面是很難扭轉的,對調試,走讀,理解代碼都會造成很大的障礙,如果你寫這樣的代碼,那代碼維護絕對會是一場 噩夢。
2). 不加限制地使用goto經常帶來錯誤或隱患。它可能跳過了某些對象的構造、變數的初始化、重要的計算等語句,例如:
goto state;
String s1, s2; // 被goto 跳過
int sum = 0; // 被goto 跳過
…..
……
state:
……
如果編譯器不能發覺此類錯誤,每用一次goto 語句都可能留下隱患。
四.Goto語句與結構化程序設計
goto語句問題的提出直接推動了結構化程序設計(structured programming)的思想和程序設計方法學的誕生和發展。結構化程序設計方法引入了工程思想和結構化思想,使大型軟體的開發和編程都得到了極大的改善。
結構化程序設計方法的主要原則可以概括為自頂向下,逐步求精,模塊化,限制使用goto語句。
1.自頂向下:程序設計時,應先考慮總體,后考慮細節;先考慮全局目標,后考慮局部目標。不要一開始就過多追求眾多的細節,先從最上層總目標開始設計,逐步使問題具體化。
2.逐步求精:對複雜問題,應設計一些子目標作為過渡,逐步細化。
3.模塊化:一個複雜問題,肯定是由若干稍簡單的問題構成。模塊化是把程序要解決的總目標分解為子目標,再進一步分解為具體的小目標,把每一個小目標稱為一個模塊。
4.限制使用goto語句
結構化程序設計方法的起源來自對goto語句的認識和爭論。肯定的結論是,在塊和進程的非正常出口處往往需要用goto語句,使用goto語句會使程序執行效率較高;在合成程序目標時,goto語句往往是有用的,如返回語句用goto。否定的結論是,goto語句是有害的,是造成程序混亂的禍根,程序的質量與goto語句的數量呈反比,應該在所有高級程序設計語言中取消goto語句。取消goto語句后,程序易於理解、易於排錯、容易維護,容易進行正確性證明。作為爭論的結論,1974年Knuth發表了令人信服的總結,並證實了:
(1)goto語句確實有害,應當盡量避免;
(2)完全避免使用goto語句也並非是個明智的方法,有些地方使用goto語句,會使程序流程更清楚、效率更高。
(3)爭論的焦點不應該放在是否取消goto語句上,而應該放在用什麼樣的程序結構上。其中最關鍵的是,應在以提高程序清晰性為目標的結構化方法中限制使用goto語句
五。關於goto使用語句的一些建議
goto語句在結構化編程技術出來后,被當作破壞結構化程序的典型代表,可以說,在結構化程序設計年代,goto語句就像洪水猛獸一樣,程序員都唯恐避之不及;可後來在微軟的一些例子程序中經常把goto語句用來處理出錯,當出錯時,goto到函數要退出的一個label那裡進行資源釋放等操作。那麼,goto語句是不是只可以用於出錯處理,其他地方都不可以用了呢?下列關於使用goto語句的原則可以供讀者參考。
1) 使用goto語句只能goto到同一函數內,而不能從一個函數里goto到另外一個函數里。
2) 使用goto語句在同一函數內進行goto時,goto的起點應是函數內一段小功能的結束處,goto的目的label處應是函數內另外一段小功能的開始處。
3) 不能從一段複雜的執行狀態中的位置goto到另外一個位置,比如,從多重嵌套的循環判斷中跳出去就是不允許的。
4)應該避免向兩個方向跳轉。這樣最容易導致"麵條代碼"。