https://m.toutiaocdn.com/group/6737995232228409860/?app=news_article×tamp=1568819883&req_id=201909182318030100260770671B1D2FAB&group_id=6737995232228409860
基本上,幾乎每一個(gè)初學(xué)者在剛接觸C語(yǔ)言時(shí),都會(huì )被告知C語(yǔ)言程序的默認入口是 main() 函數,程序總是從入口函數處開(kāi)始運行。一般來(lái)說(shuō),main() 函數有兩個(gè)常用的原型,它們的C語(yǔ)言代碼是下面這樣的:
為什么在C語(yǔ)言程序中,可以有不同類(lèi)型的 main() 函數呢?
當然,在一些比較舊的教材或者C語(yǔ)言代碼中,讀者可能還見(jiàn)過(guò) void 返回值類(lèi)型,甚至沒(méi)有寫(xiě)返回值類(lèi)型的 main() 函數原型:
void main();main();
C++程序基本上也是如此,但是 C++ 提供了重載語(yǔ)法支持,因此同一個(gè)函數具有不同的參數類(lèi)型是可以理解的。而C語(yǔ)言沒(méi)有重載語(yǔ)法,為什么在C語(yǔ)言程序中,可以有不同類(lèi)型的 main() 函數呢?
C語(yǔ)言程序支持多種類(lèi)型 main() 函數,其實(shí)和支持可變參數函數是類(lèi)似的。按照我之前文章中的討論,對于可變參數函數,例如 printf() 函數,或者未明確指定參數的函數,例如 void fun(); ,在被調用時(shí),是允許傳入任意多參數的:
上面幾行C語(yǔ)言代碼都是合法的。一般來(lái)說(shuō),C語(yǔ)言代碼被編譯為指令后,函數被調用時(shí),它的參數是按照順序入棧的——要么是從最左參數依次入棧,要么是從最右參數依次入棧,函數執行完畢后,再依次出棧。下面是一段示例指令:
示例指令
這時(shí)再來(lái)看C語(yǔ)言程序中的幾種 main() 函數類(lèi)型就簡(jiǎn)單了,如果 main() 函數時(shí)沒(méi)有傳遞參數:
int main(){ ...}那么系統直接忽略參數棧就可以了。如果某段C語(yǔ)言程序中的 main() 函數有兩個(gè)參數:
那么系統就會(huì )將 argc 和 argv 作為棧頂的兩個(gè)元素,最后在 main() 函數執行完畢后,將參數從棧中彈出就可以了。
按照這種思路,我們甚至可以定義具有三個(gè)參數的 main() 函數,下面是一段C語(yǔ)言代碼示例:
#includevoid fun(int a, int b){ printf('a=%d, b=%d\n', a, b);}int main(int a, char **b, char **c){ fun(2, 3); return 0;}
C語(yǔ)言代碼示例
編譯并執行這段C語(yǔ)言代碼,可以得到如下輸出:
這是容易理解的,因為處理三個(gè)參數的 main() 函數時(shí),系統無(wú)非就是多做一些參數入棧和出棧的操作而已。如果某個(gè)平臺需要使用 main() 函數的第三個(gè)參數(有些平臺使用 main() 函數的第三個(gè)參數作為環(huán)境指針),它只需從堆棧頂部找到第三個(gè)元素就可以了。
這種情況下,類(lèi)似的C語(yǔ)言代碼是下面這樣的:
extern int main(int argc, char **argv, char **envp);void __start(void){ /* ... */ exit( main(argc_from_somewhere, argv_from_somewhere, envp_from_somewhere));}其實(shí)到這里,讀者應該能明白了,C語(yǔ)言程序中的 main() 函數調用其實(shí)就是“約定”,只要 main() 函數不關(guān)心它的調用者傳遞的參數,那么傳什么樣的參數給 main() 都是可以得到正常工作的C語(yǔ)言程序的。
C語(yǔ)言程序中的 main() 函數調用其實(shí)就是“約定”
還有一種情況需要說(shuō)明,如果某段C語(yǔ)言程序中的 main() 函數是下面這樣的,明確指定 main() 函數沒(méi)有參數:
編譯器可能會(huì )將其做特殊處理——執行一個(gè)代碼轉換,最后實(shí)際的 main() 函數實(shí)際上是下面這樣的:
int main(int __argc_ignore, char **__argv_ignore, char **__envp_ignore){ /* ... */}也有可能編譯器會(huì )從幾種預編譯的備選方案中選擇一種支持C語(yǔ)言程序定義的 main() 函數,這種方法也是可行的。
點(diǎn)個(gè)贊再走吧
歡迎在評論區一起討論,質(zhì)疑。文章都是手打原創(chuàng ),每天最淺顯的介紹C語(yǔ)言、linux等嵌入式開(kāi)發(fā),喜歡我的文章就關(guān)注一波吧,可以看到最新更新和之前的文章哦。
聯(lián)系客服