我們知道在 C 語言中,程序是從 main 函數開始運行的,我們稱其爲主函數。我們來看看下面幾種 main 函數定義正確嗎?
那麼 main 函數的原型到底是什麼呢?我們來看看編譯器怎麼說,我們分別編譯下四種 main
函數的形式,經過編譯後,程序可以編譯通過並且執行完成。那麼最標準的 main 函數的原型是上面的第四種,main 函數是操作系統調用的函數,操作系統總是將 main 函數作爲應用程序的開始並且將 main 函數的返回值作爲應用程序的退出狀態。那麼 C 編譯器爲什麼要支持這麼多不同的 main 函數原型呢?我們來做個實驗,代碼如下
#include <stdio.h> int main() { printf("hello\n"); return 99; }
我們在 BCC 編譯器下編譯看看,結果肯定是打印 hello 了。但是我們返回的是 99,我們順便打印下環境變量的值,看看有什麼玄機
我們看到打印的環境變量的值爲 99。如果我們將上面程序中的 return 後面改成 0。再來編譯看下環境變量的值是多少
我們看到環境變量的值變成 0 了。那麼 main 函數的返回值是將它返回到系統中並保存下來。那麼回到我們之前所說的問題,在以前的程序中,好多中編程寫法。我們爲了兼容以前所有的程序,編譯器就必須得支持所有的 main 函數的寫法。那麼程序執行時可以向 main 函數傳遞參數,格式:int main(int argc, char *argv[], char *env[])。a> argc - 命令行參數個數;b> argv - 命令行參數數組;c> env - 環境變量數組;我們平時見到的大多數是帶前兩種的 main 函數的寫法。
那麼在gcc編譯器中,常見用法如下:
我們下來看個示例代碼,代碼如下
#intclude <stdio.h> int main(int argc, char* argv[], char* env[]) { int i = 0; printf("============== Begin argv ==============\n"); for(i=0; i<argc; i++) { printf("%s\n", argv[i]); } printf("============== End argv ==============\n"); return 0; }
我們來看看編譯效果
我們可以看到打印出了 ./a.out。也就是說打印出了除過 gcc 以外的命令行參數,我們再以下面這種方式來打印呢
那麼我們可以看到打印出了除 gcc 以外的所有參數。講到最後,我們再來討論個有意思的問題:main 函數一定是程序執行的第一個函數嗎?咋一聽,就是啊,我們平時書上所見到的,還有老師說的都是這樣的哈。那麼我們來做個實驗,代碼如下
#include <stdio.h> #ifndef __GNUC__ #define __attribute__(x) #endif __attribute__((constructor)) void before_main() { printf("%s\n",__FUNCTION__); } __attribute__((destructor)) void after_main() { printf("%s\n",__FUNCTION__); } int main() { printf("%s\n",__FUNCTION__); return 0; }
我們看到如果是 GUNC 編譯器的話,便定義 __attribute__ 宏。通過這個宏,我們分別聲明瞭兩個函數,我們來看看編譯結果
我們看到竟然在 main 函數的前後分別執行了這兩個函數。感覺很神奇,那麼這個 __attribute__ 宏便是我們 gcc 編譯器所特有的屬性關鍵字。使用就可以在 main 函數的執行前後再去執行別的函數。我們再來看看在 BCC 編譯器中,它還支持嗎
我們看到它不支持那個 __FUNCTION__ ,我們便將他換成對應的字符串。編譯後的結果是隻打印了 main,也就是說它在 main 函數前後並沒有去執行那兩個函數。所以這個特性也是編譯器特有支持的,但起碼說明了在現代編譯器中支持在 main 函數前調用其他函數。
歡迎大家一起來學習 C 語言,可以加我QQ:243343083。