C語言為什麼要有 main 函數?具體作用是什麼?

p.s.:在問一個問題,為什麼c要把程序的開始用main函數來表示,和用start{}end比有什麼優點?


我嘗試從另一角度解釋「為什麼」要這麼設計。

大多數腳本語言不需要標明程序開始執行的位置,例如Python、Lua。只需把程序語句直接寫在源文件中,然後執行該文件。

然而,C與許多編譯式語言分開了3個階段:編譯、鏈接、運行。每個編輯單元(例如多個.c源文件)是各自獨立編譯成目標文件(例如.o),最後由鏈接器把這些目標文件鏈接成可執行程序。

如果C容許在文件的全局域里寫語句,若只有一個編譯單元時問題不大。但對於多個編譯單元,鏈接器便不知道應先執行哪一個編譯單元的全局語句。

其中一種可行的解決辦法,就是鏈接時只容許其中一個編譯單元含有全局語句,超過一個就報錯。但更簡單的做法,是利用不能重複定義函數的機制,若鏈接時有超過1個main()函數就報錯。

使用main()函數這種設計簡單解決了上述的鏈接問題,而且不容許「全局語句」也令語言簡單一點。

P.S. C++允許利用全局對象在main()執行之前調用構造函數,會造成上述鏈接器不知先執行哪一段代碼的問題。在實際工程上也確實會造成問題,可能需要用singleton或其他方式解決。


你寫的代碼編譯後要使用鏈接器進行鏈接,總得告訴鏈接器你的代碼從哪裡開始執行吧。

main函數就是這個約定好的用戶代碼默認入口。當然,只要你願意,改成啥都行。比如你改成nomain,那麼編譯鏈接時就要指定入口了(同時指定不鏈接CRT的入口代碼)。

P.S. 但是這麼做就木有CRT替你做的初始化之類的事情了。


簡單來說,因為鏈接時需要用到。

實際上 gcc main.o -o main 是調用 ld 來做鏈接的,相當於以下的命令:

$ ld /usr/lib/crt1.o /usr/lib/crti.o main.o -o main -lc -dynamic-linker /lib/ld-linux.so.2

main.o 需要和 crt1.o,crti.o 這兩個目標文件鏈接在一起,從而生成可執行文件 main。你可以使用 readelf 命令來查看 crt1.o 文件的符號表,就會發現裡面有一個 main 符號是未定義的,因此需要別的目標文件提供一個定義並且和 crt1.o 鏈接在一起(在 crt1.o 中要用到 main 這個符號所代表的地址,而 crt1.o 中的未定義符號 main 在 main.o 中定義了)。

事實上,整個可執行程序真正的入口點是 crt1.o 中的 _start,而 main 函數是被 _start 調用的。


你有一所大房子,那麼多房間,裝修得漂漂亮亮。

早晨陽光會灑進來,暖暖的一片,一直到日落。

風會填滿每一間屋子,然後從另一扇窗跑掉。

房子就在那裡,藏在那扇門的背後。

你要回家,你要打開那扇門。

main就是那扇門。


1 main是用來指定程序從那裡開始執行的。就是一個大家遵守的規定。(被寫到了C99規範)

從編譯器的角度來看,你可以自己定義你喜歡的main1, main2 或者其他什麼函數。只要沒有重名,c的編譯器都能給你編出來。(生產結果是obj文件,而不是可執行文件)

所以你問為什麼c語言要main函數,其實c語言並不是那個在乎main函數。很多開發平台因為不喜歡main這個名字,也會把入口函數改成其他名字。

2 就好像c的編譯器工作之前先要做cpp(比如頭文件展開)一樣,編譯完了以後還要鏈接成可執行文件。

鏈接的時候需要知道程序的入口是什麼。不管是什麼總歸得有的名字,所以main被選中了。

你不喜歡這個名字,可以指定你自己的入口。

參見如下文檔的 -e 或者--entry參數。

https://sourceware.org/binutils/docs-2.21/ld/Options.html#Options

3 很多腳本語言是不要入口函數的。比如什麼函數都不寫,直接寫一句print 1000就能執行了。

那個他們怎麼就知道入口函數是什麼呢?因為他們第一行開始執行,因此也是很明確的。

4 知乎經常有人批評別人說,問為什麼之前請先問是不是。

因此,你的問題假如改成「c語言的入口函數名是不是只能為main」,會得到更精準的答案。


從CPU角度來看,將要執行的指令地址放在程序計數器(PC)里,一個程序需要執行,總需要一個入口地址。通用的可執行文件格式(ELF/PE)總會指定一個入口地址,這樣CPU才能開始執行一個程序的指令(由操作系統調度),所以不管什麼語言生成的可執行文件都需要一個入口地址。C語言自然也會有,至於為什麼是main函數,這是C語言標準規定,其實這個函數地址並不是一個可執行文件的真正的入口地址,通常入口地址在C Runtime里,由C Runtime調用main函數,而這個被調用的函數可以在鏈接的時候指定,不必須是main,C Runtime在這裡的作用是做一些C語言的環境準備工作,比如打開stdin/stdout/stderr三個文件。對其它語言來說自然也會有一個規定的入口函數。


Genesis 1:1 In the beginning God created the heaven and the earth.


讓你在大腦一片空白的情況下,至少能充滿自信的寫下代碼的第一行。

就像當年做數學題時優雅地寫下的那個「解」一樣。


不同層面這個問題有不同的解釋,不過意思都差不多。從某層面來說,可以認為main函數是別人調用你這個程序的公開介面。


C語言沒說C程序一定要有main()函數。


WinMain函數表示情緒穩定


作為初學者可以這麼理解,程序運行需要一個開始的標誌。

哈佛大學的CS50課程中用過scratch這個程序,作為一個極好的編程入門工具,scratch採用的方式是點擊小綠旗,而在c語言中它是main函數。


C語言程序不一定非得有main函數。

C語言標準在一開始(C90標準 5.1.2條),就規定了程序的執行環境。

對於沒有操作系統的環境來說,C程序的入口函數是什麼都可以(也就是說的在單片機的C程序里,或者在操作系統的底層代碼的C入口處,不需要是main函數)。

對於有操作系統的環境來說(C入門者學習C的環境),C程序的入口是main函數。而且聲明為以下兩者之一

int main(void);

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

對於 操作系統的執行環境的具體實現來說,鏈接器會把「你寫的C程序(以main開頭)」和另外的啟動程序相鏈接,而那些啟動程序裡面會引用你寫的main函數。這樣從程序員的視角來看,「C程序的入口」是main。

下圖是標準的中文翻譯版原文:


我剛剛弄懂main函數的參數是幹嘛用的,想找個地方發一下我所發現的,於是找到了這個問題。我用的是C#,不過所有編程語言應該都差不多。大神勿噴!!

——————————————————————————————————————————

那些網友的回答裡面都說了,main函數是應用程序的入口。而main函數的參數其實是用戶給它的參數。比如我們在命令提示符中輸入「shutdown /a」來中止關機,我們可以這樣理解這條指令:調用shutdown程序的main,給它傳一個值為「/a」的字元串。簡單的說就是:

shutdown.main("/a");

———————————————————————————————————————————

main函數的參數不單單用於dos環境,在圖形界面應用程序中也很有用。windows下右鍵一個文件,會有一個「打開方式」的選項

我做了一個圖片處理文件,它的主函數是醬紫的:

static void Main(string[] file)
{

if (file.Length&>0)
{
data.picture = new Bitmap(file[0]);
}
else
{
data.picture = Properties.Resources.小青圖圖;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}

有什麼效果呢?我們右擊一張圖片,就這張愛因斯坦的圖片吧:

選擇打開方式——》選擇其它應用

沒錯,我做的這個圖像處理工具的名稱就叫「小青圖圖」

成功打開!

所以說,windows用一個應用程序打開一個文件的本質其實是調用應用程序的main函數,並給應用程序的main函數傳一個字元串參數,這個字元串的值為你想打開的文件的路徑。


main函數,就是執行時把這個程序裝入任務調度器中,調度器執行調度的入口函數而已,而main函數只是個程序員和調度器之間的約定。就這麼簡單。


CLI 程序運行的入口


c語言可以沒有main函數,做嵌入式深有體會


c並沒有說一定是從main開始,只是用的人多了自然就成了約定。你像更改c0s.obj之後你可以讓c不從main開始,還可以改寫crt0.s也可以達到同樣的效果,只不過是兩個平台罷了,另外有些編譯器已經把_main作為特定的值看待,即使你沒寫_main的功能:


C語言main函數是個約定俗成的名字,但不代表總是這樣,程序總要有個入口,為了更加通用,當然都應該希望入口是統一的名字。 至於不用main的情況,在嵌入式編程中經常見到,由於嵌入彙編的緣故,入口在彙編代碼中,再跳向C語言入口的時候,這個名字由彙編代碼決定跳轉時使用的外部標籤決定,你不必一定要讓這個標籤為main,比如linux內核的C語言入口就是start_kernel,而非main。。。


可以看看這本書 程序員的自我修養 (豆瓣)


推薦閱讀:

如果scanf的格式指示符是%f,賦給一個double型的變數,在內存層面上會發生什麼?
指針可以修改const修飾的變數么?
int (*pf)(1024)為什麼是函數調用?
為什麼 Windows API 使用 stdcall 調用約定?
c++中的字元串常量為什麼可以賦值給char*?

TAG:編程語言 | 編程 | C編程語言 | C | CC |