函式中為什麼不要有多次return以及其解決方法
阿新 • • 發佈:2018-12-04
函式中隨處return,是造成我們資源洩露和程式死鎖的主要根源。很多同志寫過類似的程式碼,函式中建立了和引用了多個資源,中間使用的過程中出錯了,程式return,經典的程式碼是這樣的:
void fun() { Lock(mutex); mem = malloc(size); if (null == mem) { return; //死鎖 } fh = fopen(“test.txt”); if (fh) { return; //死鎖+記憶體洩露 } if (fwrite(“abc”, fh) < 0) { return; //死鎖+記憶體洩露+控制代碼洩露 } fclose(fh); free(mem); Unlock(mtex); }
上述程式碼中的資源洩露是顯而易見的,程式設計新手卻很容易寫出這樣的程式碼,但問題卻不容易發現,因為異常流程通常不會發生,而一旦發生了,就是一場噩夢:不好跟蹤也不好重現。對吃過這種虧程式設計老手而言,處理起來會格外小心,而是有了如下版本的程式碼(我們的工程中類似的程式碼隨處可見):
void fun() { Lock(mutex); mem = malloc(size); if (null == mem) { Unlock(mutex); //鎖解除了,很好 return; } fh = fopen(“test.txt”); if (fh) { Free(mem); Unlock(mutext); //錯誤處理加了一行 return; } if (fread(mem, size, fh) < 0) { Free(mem); Flcose(fh); Unlock(mutext); //錯誤處理又加了一行… return; } fclose(fh); free(mem); Unlock(mtex); }
這個版本的程式碼沒什麼邏輯問題,在每個異常環節,把該處理的都處理完了。看起來非常完美,卻依然存有大坑。首先,我們的異常處理流程越來越長,重複的程式碼像火車車廂一樣越串越長,程式碼維護非常不方便,某天其中一個異常處理流程要修改了,我們需要修同步多處,萬一漏了某處(事實上不是萬一,而是經常),問題沒有修改徹底;更為嚴重的是,冗於的異常處理會讓大家感到疲勞,某一天函式中又要加入新的資源引用或異常處理邏輯時,在先驅的引導下,我們的人依然還是會犯同樣的錯誤:要麼什麼都不管提前返回,或者處理不充分就走了,結果還是同樣的資源洩露。
如何解決,Linux核心程式碼給了我們很好的解決方案,合理地使用goto語句,將異常處理定向到統一的地方,既解決了資源洩露隱患,也保持了正常流程程式碼的簡潔性,這是改進後的版本:
void fun()
{
Lock(mutex);
mem = malloc(size);
if (null == mem)
{
goto ERR_EXIT1;
}
fh = fopen(“test.txt”);
if (fh)
{
goto ERR_EXIT2;
}
if (fread(mem, size, fh) < 0)
{
goto ERR_EXIT3;
}
ERR_EXIT3:
fclose(fh);
ERR_EXIT2:
free(mem);
ERR_EXIT1:
Unlock(mtex);
}
總結:以後新寫C程式碼的異常處理,統一採用上面的模版。C++/java有更好的異常處理機制,上述方案只供參考,但解決思路應該是一致的,隨處return與冗餘異常處理應該被嚴厲禁止。