c语言编译、汇编、链接、执行过程分析
1、c程序的编译过程一般为:
- 预处理
- 编译成汇编代码(生成 .s 文件)
- 汇编成目标代码(生成 .o 文件)
- 链接 (生成 .exe)
2、举例分析
c编译器基本都是以c文件为单位进行编译的,也就是说当编译器在编译某个c文件的时候,编译器并不知道项目中有其他的c文件的存在。c编译器只针对当前正在编译的c文件,它并不知道还有多少个其他的c文件,也不知道这些c文件之间的关系(主要是调用关系)。关于这一点,在之前的博文中有讲过,见:http://blog.****.net/baidu_35679960/article/details/66973829
因此,当某个c文件中引用了(如访问或者是调用)其他c文件中定义的全局变量或者是函数时,编译器并不清楚被引用的全局变量或函数是否真的存在,存在于何处,那么编译器如何解决这个问题呢?对于这种情况,编译器要求被编译的c文件必须采用extern关键字显示的声明这些外部定义的全局变量或函数,告诉编译器这些变量和函数来自于当前被编译的c文件之外。
对多个c文件在linux下通过gcc编译器进行手动编译链接,而不是在IDE下去操作,能够增深对这一过程的了解,通过这样的方式也有利于学习makefile的书写。具体来说:
1、// 新建文件:add.c,min.c,head.h,main.c,内容如下: //add.c int add (int a, int b){ return a+b; } //min.c int min(int a, int b){ return a-b; } //head.h extern int add(int a, int b); extern int min(int a, int b); //main.c #include <stdio.h> #include "head.h" int main(){ printf("add result = %d\n", add(2, 3)); printf("min result = %d\n", min(2, 3)); return 0; } 2、将add.c和min.c编译成目标文件 gcc -c add.c -o add.o gcc -c min.c -o min.o ps:上面这两条指令可以合并为:gcc -c add.c min.c 3、将main.c链接 .o文件生成可执行文件 gcc main.c add.o min.o -I. -o main ps:上面这条命令可以进一步拆分为两条指令: gcc -c main.c -I. -o main.o gcc main.o add.o min.o -o main 4、执行可执行文件 ./main 5、输出: add result = 5 min result = -1
3、额外说明
c语言中函数应该先声明,再使用,这是推荐的情况,在vc6中也是这么实现的,但是gcc编译器稍有不同,见博文:C语言中,函数不声明也能使用,但会出现warning: implicit declaration of function,在linux的gcc编译器下,自定义函数未经声明却能在主程序中被调用,程序也能正常运行(有warning:implicit declaration of function)。这是因为在C语言中,当函数在调用函数之前没有声明或定义,默认作为隐式声明处理,只要在调用函数之后定义,或在别的模块中定义并编译成库文件,该库文件在调用函数所属模块编译时载入,程序即可正常运行。
c语言在gcc编译器下自定义函数不声明可以在主函数中使用,但是在c++在g++编译器中不可以,因为g++使用C++的规则:函数在被调用前必须声明或定义。c++语言中一般的操作是:类定义在头文件中,假设为Class.h;类中成员函数的实现定义在源文件中假设为Class.cpp,并且include "Class.h";其他使用类的文件也应该include "Class.h",详见:为什么类的定义应当写在头文件中,从而被多个源文件包含?
参考:
[1] C语言编译原理介绍