如果 main 函數的末尾沒有 return 語句將會有什麼影響

2021-02-20 泰曉科技
背景簡介

本文是前段在知乎回答的一個問題,覺得蠻重要的,重新編撰發布如下。

原問題為:

"c語言中,如果main函數的末尾沒有return語句將會有什麼影響?":

我是準大一,學計算機的,剛剛接觸計算機,萌新求解答

原回答請參考這裡: https://www.zhihu.com/question/338814178/answer/785578903。

問題的本質

回答這個問題其實只要理解一個東西就行了:

那就是帶有返回值的函數請務必提供返回值,這個是基本約定也是編程習慣,如果大家都遵守,你不遵守,各種奇葩和頭疼的後果就遲早會來,不管是 main 也好,其他函數也好。

所以,準大一這個時候一定要從現在開始養成習慣,遵守語言的基本約定。

main 的標準聲明

main 的標準聲明是什麼?

int main(int argc, char *argv[])

很多同學寫例子,也有這麼寫的?

void main(void) { }

實際上,完整的 main 聲明還有第三個參數 env,這裡不做展開。

C 語言各標準差異以及靜態檢測方法

這種可以用 -Wall 檢查出來:

$ echo 'void main(void) {}' | gcc -Wall -x c - -
<stdin>:1:6: warning: return type of 『main』 is not 『int』 [-Wmain]

如果用 int 默認檢查不出來 return value ,默認是兼容 c11 的  gnu11:

$ echo 'int main(void) {}' | gcc -Wall -x c - -
$ echo 'int main(void) {}' | gcc -Wall -std=gnu11 -x c - -

試了下,c99 之後都是沒有提示問題。但是 c90 以及之前都提示需要加返回值:

$ echo 
<stdin>: In function 『main』:
<stdin>:1:1: warning: control reaches end of non-void function [-Wreturn-type]

考慮到兼容性,建議務必養成寫法上的習慣。由於 main 稍微特殊些,c99 默認處理了,但是對於其他函數還是要自己加,不做處理的話,是有很大風險的。

對於 c99 以及之後的版本,對普通函數還是會做檢查。如果有的寫,有的不寫,不一致的話久而久之習慣就很難養成了。

$ echo 
<stdin>: In function 『test』:
<stdin>:1:1: warning: control reaches end of non-void function [-Wreturn-type]

由於人總會出錯,所以,在編譯的 cflags 裡頭,建議強制加上 -Wall -Werror,在遇到這類錯誤是強制退出,而不僅僅是警告。當然,對於已經存在的項目,prove-in-use 的情況下,建議保留之前的默認配置選項,除非要確實投入很多精力去重構。

$ echo 
<stdin>: In function 『main』:
<stdin>:1:1: error: control reaches end of non-void function [-Werror=return-type]
cc1: all warnings being treated as errors

關於返回值的含義

需要注意的是,默認情況下,沒有錯誤,就是 return 0,而不是 return 1。

完整的模板:

int main(int argc, char *argv[])
{
    return 0;
}

更多關於 main 和 C 函數入口的討論,歡迎訪問:"GCC 編譯的背後": http://tinylab.org/behind-the-gcc-compiler/ 以及 《360° 剖析 Linux ELF》視頻課程。

上面多次提到了後果和風險,為什麼?因為檢查函數和程序返回值是一個最最基礎的操作。如果不按常理返回正確的返回值,後果是,後續對該值的檢查所做出的所有動作都可能不可預知。

這個值是怎麼體現的呢?

$ echo -e 'int main(void){ return 0; }' | gcc -Wall -Werror -std=c99 -O0 -x c - -
$ ./a.out
$ echo $?
0
$ echo -e 'int main(void){ return 100; }' | gcc -Wall -Werror -std=c99 -O0 -x c - -
$ ./a.out
$ echo $?
100

比如說,在 Shell 編程中通常會在程序執行完,立即檢查返回值,然後決定接下去做什麼操作?

if [ $? -eq 0 ]; then echo 'Success'; else echo 'Failure'; fi

關於 Shell 的布爾操作,可以參考:Shell 編程範例 (3) : 布爾運算。

相關焦點

  • C語言裡,main 函數中 return x和 exit(x) 到底有什麼區別?
    問題:C語言裡,main 函數中 return x和 exit(x) 到底有什麼區別 ?
  • main( )函數詳解
    C的設計原則是把函數作為程序的構成模塊。main()函數稱之為主函數,一個C程序總是從main()函數開始執行的。    return 0;}int main( int argc, char *argv[] ) /* 帶參數形式 */{    ...    return 0;}int指明了main()函數的返回類型,函數名後面的圓括號一般包含傳遞給函數的信息。void表示沒有給函數傳遞參數。
  • 深度剖析C語言的main函數
    void main()有一些書上的,都使用了void main( ) ,其實這是錯誤的。C/C++ 中從來沒有定義過void main( ) 。可能正是因為這個,所以很多人都誤認為如果不需要程序返回值時可以把main函數定義成void main(void) 。然而這是錯誤的!main 函數的返回值應該定義為 int 類型,C 和 C++ 標準中都是這樣規定的。雖然在一些編譯器中,void main() 可以通過編譯,但並非所有編譯器都支持 void main() ,因為標準中從來沒有定義過 void main 。
  • 為什麼 Python 沒有 main 函數?
    以下為譯文:眾所周知,Python中沒有所謂的main函數,但是網上經常有文章提到「 Python的main函數」和「建議編寫main函數」。其實,可能他們是想模仿真正的main函數,但是許多人都被誤導(或誤解),然後編寫了非常笨拙的代碼。在本文中,我們來討論一下為什麼Python沒有main函數。在開始討論之前,我們先來回答以下兩個問題:所謂的「main函數」究竟是什麼意思?為什麼有些程式語言必須編寫main函數?
  • 深度剖析C語言的main函數!
    main函數的返回值用於說明程序的退出狀態。如果返回0,則代表程序正常退出。返回其它數字的含義則由系統決定。通常,返回非零代表程序異常退出。void main()有一些書上的,都使用了void main( ) ,其實這是錯誤的。C/C++ 中從來沒有定義過void main( ) 。
  • 深度剖析C語言的main函數
    main函數的返回值用於說明程序的退出狀態。如果返回0,則代表程序正常退出。返回其它數字的含義則由系統決定。通常,返回非零代表程序異常退出。void main()有一些書上的,都使用了void main( ) ,其實這是錯誤的。
  • 乾貨 | 深度剖析C語言的main函數
    main函數的返回值用於說明程序的退出狀態。如果返回0,則代表程序正常退出。返回其它數字的含義則由系統決定。通常,返回非零代表程序異常退出。void main()有一些書上的,都使用了void main( ) ,其實這是錯誤的。
  • 第52節:程序後續升級修改的利器,return語句鮮為人知的用法
    開場白:return語句經常用在帶參數返回的函數中,字面上理解就是返回的意思,因此很多單片機初學者很容易忽略了return語句還有中斷強行退出的功能。(3)原始碼講解如下:#include "REG52.H"#define const_voice_short 40 //蜂鳴器短叫的持續時間#define const_rc_size 10 //接收串口中斷數據的緩衝區數組大小#define const_receive_time 5 //如果超過這個時間沒有串口數據過來,就認為一串數據已經全部接收完
  • C語言for,while,if-else,return語句的綜合使用
    #includevoid main(){int i,j,k;j=10;while(j--) //j控制換行{ i=10-j;while(i--) //i控制循環,而且控制的是緊隨其後的語句,即printf("*");而控住不了printf("");if(i<3) //if-else的用法是滿足if則執行其後的語句,否則執行else。
  • C語言主函數main函數返回值有什麼用?
    使用了這麼多年的C語言,學習工作,開發伺服器程序,始終沒有覺得main函數的返回值有什麼作用。最近一段時間由於開發一個測試軟體,需要對考生提交的C源碼和編譯生成的EXE可執行文件進行檢驗,才讓我狠狠地理解了一下main的返回值。
  • Java finally語句到底是在return之前還是之後執行?
    我也是一頭霧水,我覺得他們的說法都不正確,我覺得應該是:finally語句是在try的return語句執行之後,return返回之前執行。這樣的說法有點矛盾,也許是我表述不太清楚,下面我給出自己試驗的一些結果和示例進行佐證,有什麼問題歡迎大家提出來。1.finally語句在return語句執行之後return返回之前執行的。
  • 「C語言」int main還是void main?
    剛好最近有非計算機相關專業學C的同學問我```int main```、```main```和```void``` ```main```有什麼區別,便覺得是時候好好整理一番了,於是有了此文。再比如,用於專業環境的程序如機器人中的控制晶片--可能不需要main函數),是要求有返回值的,該返回值返回給作業系統來表明改程序的執行狀況。返回0代表程序正常執行成功,返回非0值代表程序異常結束,因此返回值需要是int整型,於是有了```int main()```的規範。
  • 啟動一個沒有 main 函數的 java 程序
    作為一名 JAVA 開發者,不知道大家有沒有去想過,JAVA  程序為什麼一定要從 main 函數執行開始,其實關於這個話題,我大概從網上搜了下,其實不乏有 main 方法是我們學習Java語言學習的第一個方法,也是每個
  • 如何寫好 C main 函數 | Linux 中國
    與其使用注釋,不如使用有意義的函數名和變量名。鑑於程式設計師固有的惰性,一旦添加了注釋,維護負擔就會增加一倍。如果更改或重構代碼,則需要更新或擴充注釋。隨著時間的推移,代碼會變得面目全非,與注釋所描述的內容完全不同。如果你必須寫注釋,不要寫關於代碼正在做什麼,相反,寫下代碼為什麼要這樣寫。
  • Java筆試面試總結—try、catch、finally語句中有return 的各類情況
    一、try-catch  語句塊我們可以看看下面程序:public static void main(String[] args) {    System.out.println(handleException0());  }  /**   * try,catch都有return   * @return
  • 《和孩子一起學C++》cout語句的使用
    #include <iostream>using namespace std;int main() {return 0;}一個C++程序由頭文件、命名空間和主函數構成。頭文件類似於這種形式:#include <iostream>,是C++對其他程序的引用,這樣就可以在我們的程序中調用引用程序中的功能(函數)。
  • 好程式設計師Python培訓分享print和return的作用及區別
    print不會以任何方式影響函數。它只是為了幫助人類使用函數。它對於理解程序如何工作非常有用,並且可以在調試中用於檢查程序中的各種值而不會中斷程序。 return是函數返回值的主要方式。所有函數都將返回一個值,如果沒有return語句,它將返回None。函數返回的值可以作為參數進一步傳遞給另一個函數、存儲為變量,或者只是為了人類用戶的使用而列印。return旨在立即中斷控制流並退出當前函數,將指定值返回給調用函數的調用者。
  • 循環裡continue,break,return的作用,你知道嗎?
    Break 語句在循環中,關鍵詞Break,作用是跳出循環,一般有以下兩種用法:當 break 語句出現在一個循環內時,循環會立即終止,且程序流將繼續執行緊接著循環的下一條語句它可用於終止 switch 語句中的一個 case。如果使用的是嵌套循環,break 語句會停止執行最內層的循環,然後開始執行該塊之後的下一行代碼。注意:break語句對if-else的條件語句不起作用。
  • 好程式設計師Python培訓分享print和return的作用及區別解析
    print不會以任何方式影響函數。它只是為了幫助人類使用函數。它對於理解程序如何工作非常有用,並且可以在調試中用於檢查程序中的各種值而不會中斷程序。 return是函數返回值的主要方式。所有函數都將返回一個值,如果沒有return語句,它將返回None。函數返回的值可以作為參數進一步傳遞給另一個函數、存儲為變量,或者只是為了人類用戶的使用而列印。return旨在立即中斷控制流並退出當前函數,將指定值返回給調用函數的調用者。
  • Async/Await有什麼用?
    在異步函數中編寫代碼時,可以使用 await 語句。該語句在異步函數外部不可用。我們無法在 main 的作用域內獲得 Promise 的值。下面是相同的程序,但是用了 async 函數和 await 語句。