HOWTO · C++
在 C++ 中解決控制到達終點的非 void 函式錯誤
了解 C++ 非 void 函式為何可能執行到其結尾大括號、如何涵蓋每條回傳路徑,以及何時適合使用備用值或編譯器警告。
本頁內容
C++ 函式的回傳型別若不是 void,就必須在每條可到達的路徑上產生一個值;例外是執行到 main 的結尾時,會隱含回傳 0。若其他非 void 函式的某條可到達路徑在沒有以 return 回傳值的情況下走到右大括號,行為便是未定義的;視程式碼和警告設定而定,編譯器也可能指出這條路徑。要修正問題,請逐一追蹤各個分支,並讓回傳值符合函式的用途。 (非 void 函式)
認識這個錯誤
非 void 函式承諾會回傳其宣告型別的值。通常稱為「control reaches end of non-void function」的編譯器診斷,指出某條路徑可能在未回傳該值的情況下走到右大括號。問題不只是函式底部少了一個陳述式:前面的條件判斷可能讓某些輸入沒有結果。例如,int add(int a, int b) { if (a > b) return a + b; } 在 a <= b 時不會回傳值。非 main 的非 void 函式若因此走到結尾,行為是未定義的;這不代表一定會發生特定的當機或產生特定結果。
請執行以下範例:
#include <iostream>
int add(int a, int b) {
return a + b;
}
int main() {
std::cout << add(5, 3) << '\n';
}
8
這個程式會針對此輸入回傳總和。上方的單行版本呈現了造成診斷的原因;可執行版本則透過為每種輸入都回傳值來修正問題。
確保所有路徑都會回傳值
逐一檢查條件,並判斷每種情況應代表什麼。對加法而言,無論運算元的順序如何,都應回傳兩者的總和。如果條件判斷還有其他用途,可以在最後加上 return,處理其餘路徑;如果兩個分支執行的工作相同,就移除條件判斷:
#include <iostream>
int add(int a, int b) {
if (a > b) {
return a + b;
}
return a + b;
}
int main() {
std::cout << add(3, 5) << '\n';
}
當不同情況代表不同意義時,完整列出分支會很有幫助。以下範例涵蓋兩條路徑,並回傳較大的運算元:
#include <iostream>
int larger(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
int main() {
std::cout << larger(5, 3) << '\n';
}
也要檢查巢狀條件和 switch 陳述式:非 void 函式的每條執行路徑都必須回傳值,或以其他方式確保不會走到右大括號。
使用預設回傳值
對於不符合先前條件的情況,可以在最後用 return 提供備援值。只有當該值符合函式的契約時,才應這麼做。例如,這個查詢會在名稱相符時回傳已知分數,名稱不存在時則回傳 0;只有在應用程式將查無分數定義為零時,這個備援值才合適。
#include <iostream>
#include <string>
int scoreFor(const std::string& name) {
if (name == "Ada") {
return 10;
}
return 0;
}
int main() {
std::cout << scoreFor("Ada") << '\n';
std::cout << scoreFor("Lin") << '\n';
}
如果「不存在」本身代表一種不同的結果,回傳 0 可能會掩蓋這項差異。請選擇能表達實際契約的回傳型別與結果,例如可選值或明確的狀態。不要只為了消除診斷訊息,就加入沒有明確意義的備援值。
使用編譯器警告進行除錯
編譯時啟用警告,讓編譯器協助找出值得檢查的路徑。例如,g++ -Wall -Wextra -Wreturn-type your_code.cpp -o your_program 會要求 GCC 回報多種警告,適用時也包括與回傳路徑相關的診斷。不同編譯器、版本、選項和程式碼會影響警告是否出現及其措辭;警告是追查控制流程的線索,不代表所有編譯器都會以相同方式處理程式碼。
檢查每個分支,包括巢狀的 if 陳述式和 switch 的每個案例。接著,判斷函式對每種輸入應如何處理,並回傳符合該行為的值。開發期間持續啟用警告,有助於更容易注意到類似的路徑問題。
結論
如果 C++ 非 void 函式可能在沒有回傳值的情況下走到右大括號,請追蹤該路徑並修正函式邏輯。為每種情況提供符合函式用途的回傳值。只有在備援值有明確意義時才使用它,並把編譯器警告當作尋找待檢查路徑的提示。
常見問題
什麼情況會造成「control reaches end of non-void function」診斷?
可回傳值的函式中,有一條可到達的路徑在未回傳值的情況下走到右大括號。編譯器可能會指出這種情況,但診斷內容和建置行為取決於編譯器及其選項。
每個 if 分支都需要 return 嗎?
不需要。可以在條件判斷後加上 return 來處理其餘路徑。重點是不能讓任何可到達的路徑在沒有回傳值的情況下直接走到函式結尾。
我應該加上 return 0; 來消除警告嗎?
只有當零符合函式契約所定義的有效結果時才應如此。否則,請選擇能準確表示缺少情況的結果或回傳型別。
編譯器警告能指出缺少回傳值的路徑嗎?
警告可以指出某個函式可能直接走到結尾。請用它來檢查控制流程;不同編譯器和設定的警告文字與行為可能不同。