搬運自我的CSDN https://blog.csdn.net/u013213111/article/details/88537509
學習了一下makefile的寫法,跟我一起寫 Makefile(一),順便看看源文件是怎么變成可執行程序的。
GCC干了些什么事情呢?在CSAPP的1.2節中講得很清楚了:

gcc有四個階段:預處理——編譯——匯編——鏈接。於是在makefile中可以明顯地看到最終的可執行目標程序依賴於一堆.o文件(可重定位目標程序),這一堆.o文件是由鏈接器ld來進行組合打包的,而.o文件又依賴於對應的.c文件(源文件)。
所以,假如有多個源文件,例如有main函數在hello.c中,在main函數中又分別調用了位於msg1.c和msg2.c中的msg1函數和msg2函數,那么gcc在編譯hello.c的過程中其實是“不涉及”msg1.c和msg2.c文件的,要用gcc hello.c msg1.c msg2.c -o hello這樣的命令分別生成三個源文件對應的.o文件,再由鏈接器把.o文件們打包組合成hello這個可執行程序。
那么就有個地方要注意了:在C語言中,函數在調用前不一定非要聲明。如果沒有聲明,那么編譯器會自動按照一種隱式聲明的規則,為調用函數的C代碼產生匯編代碼。
會產生什么樣的問題呢?參考萬惡之源:C語言中的隱式函數聲明,當隱式聲明函數名稱恰好在鏈接庫中存在,但返回非int類型時,就會導致錯誤。所以呢,強烈建議程序員重視編譯器給出的關於隱式聲明的警告,及時通過包含必要的頭文件來消除這種警告。
來看看實例吧:
1 //hello.c 2 #include <stdio.h> 3 int main() 4 { 5 printf("Hello, world!\n"); 6 msg1(); 7 msg2(); 8 return 0; 9 }
1 //msg1.c 2 #include <stdio.h> 3 void msg1(void) 4 { 5 printf("the message sent from msg1\n"); 6 }
1 //msg2.c 2 #include <stdio.h> 3 void msg1(void) 4 { 5 printf("the message sent from msg2\n"); 6 }
在hello.c中沒有聲明msg1和msg2這兩個函數,用gcc hello.c msg1.c msg2.c -o hello命令得到這樣的警告:
hello.c: In function ‘main’: hello.c:5:3: warning: implicit declaration of function ‘msg1’ [-Wimplicit-function-declaration] msg1(); ^ hello.c:6:3: warning: implicit declaration of function ‘msg2’ [-Wimplicit-function-declaration] msg2(); ^
但是仍然生成了hello可執行文件,執行一下,輸出正常:
Hello, world! the message sent from msg1 the message sent from msg2
那么假如在編譯的時候沒有加入msg2.c會提示什么呢?gcc hello.c msg1.c -o hello看看,除了之前的警告信息外,還有error: ld returned 1 exit status,鏈接器ld找不到要依賴的文件了。
hello.c: In function ‘main’: hello.c:5:3: warning: implicit declaration of function ‘msg1’ [-Wimplicit-function-declaration] msg1(); ^ hello.c:6:3: warning: implicit declaration of function ‘msg2’ [-Wimplicit-function-declaration] msg2(); ^ /tmp/ccYSfgV0.o: In function `main': hello.c:(.text+0x27): undefined reference to `msg2' collect2: error: ld returned 1 exit status
上面提到的隱式函數聲明,在C++中是沒有的,對未聲明的函數進行調用會無法通過編譯,把gcc改為g++再來試試,g++ hello.c msg1.c msg2.c -o hello,果然error了:
hello.c: In function ‘int main()’: hello.c:5:8: error: ‘msg1’ was not declared in this scope msg1(); ^ hello.c:6:8: error: ‘msg2’ was not declared in this scope msg2(); ^
對了,關於頭文件和源文件的關系還有一篇文章可以參考:.c和.h文件的區別
