搬运自我的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文件的区别