首先构思这个C程序的作用,干什么,比如我想打印“hello world!”
进行简单的程序设计,比如我们需要设计一个打印hello word的C语言程序,需要打印函数。不同的程序有不同的设计方案,在c语言程序设计中讲了很多。
编辑是通过编辑器,实现C语言代码的编写,即编辑器提供了编写C语言代码的平台;
比如:
我们定义头文件 headtest.h:
#include
编写C文件 Untitled-1.c:
#include "headtest.h"
int main()
{
/* 我的第一个 C 程序 */
printf("Hello, World! \n");
return 0;
}
就会生成:
在编辑好代码之后,会生成对应的.c文件和.h文件。
输入命令: gcc Untitled-1.c -o hello 程序进入C程序的编程通道,输出hello.exe
一个典型的C程序编译管道,包含预处理、编译、汇编、链接四个环节。
GCC 在第一个阶段会调用预处理器 cpp 来对 C 源程序进行预处理
预处理一般有三个作用:宏定义,文件包含,条件编译。
I.宏定义简单的来说就是”#define …”,就是将程序中的某个常量全部替换成某个数据;
II.文件包含简单的来说就是”#include …”,就是在文件目录中找到include后的头文件;
III.条件编译指该程序要符合某个条件才运行,否则不运行。
也就是说,我们一般可以简单理解为:以井号’#’开头的 语句就是预处理指令。这些指令会在预处理的时候解释,拷贝对应的文件来覆盖#语句。在预处理的时候GCC还会删除注释。
我们可以通过命令:gcc Untitled-1.c -o hello.i -E 来获取这个预处理之后的文件,就是.i 文件。
打开大概看看里面吧:
处理之后引入了很多包
主程序也没有了注释。
将经过预处理之后生成的.i 文件进一步的翻译,它包括词法和语法的分析, 最终生成对应硬件平台的汇编语言,具体生成什么平台 的汇编文件取决于所采用的编译器,如果用的是 GCC,那么将会生成 x86 格式的汇编文件,如果用的是针对 ARM 平台的交叉编译器,那么将会生成 ARM 格式的汇编文件。
也就是将C语言这种高级语言编译成汇编语言,不同的编译器会有不同格式的汇编文件。
我们可以通过命令:gcc hello.i -o hello.s -S 来获取这个编译好的文件,也就是.s文件。
打开简单看看:
代码变得精简短小,不再是看得懂的C语言了
编译器 gcc 调用汇编器 as 将汇编源程序翻译成 为可重定位文件。汇编指令跟处理器直接运行的二进制指令流之间基本是一一对应的关系, 该阶段只将.s 文件里面的汇编翻译成相应的指令。
我们拿到了编译成汇编语言的.s文件,接下来就是进一步的翻译,也就是变成计算机可以看得懂的指令
我们可以通过命令:gcc hello.s -o hello.o -c 获得汇编后的文件,也就是.o文件。
打开简单看看:
emmmmm看不懂了
根据说明:
.o 文件是一个 ELF 格式的可重定位(relocatable)文件,所谓的可重定位,指的是该文件虽 然已经包含可以让处理器直接运行的指令流,但是程序中的所有的全局符号尚未定位,所谓 的全局符号,就是指函数和全局变量,函数和全局变量默认情况下是可以被外部文件引用的, 由于定义和调用可以出现在不同的文件当中,因此他们在编译的过程中需要确定其入口地 址,比如 a.c 文件里面定义了一个函数 func( ),b.c 文件里面调用了该函数,那么在完成第 三阶段汇编之后,b.o 文件里面的函数 func( )的地址将是 0,显然这是不能运行的,必须要 找到 a.c 文件里面函数 func( )的确切的入口地址,然后将 b.c 中的“全局符号”func 重新定 位为这个地址,程序才能正确运行。
那么定位这个地址,就是下一个阶段,连接:
链接系统的标准 C 库、gcc 内置库等基本库文件。
使用命令:gcc hello.o -o Untitled-1 -lc -lgcc 链接生成可执行文件,-lc 和-lgcc 是默认的,可以省略。
根据说明:
链接还会合并相同权限的段(section)。一个可执行镜像文件可以由多个可重定位文件链接而成,比如 a.o,b.o,c.o 这三个可重定位文件链接生成一个叫 做 x 的可执行文件( ELF 格式)ELF 格式是符合一定规范的文件格式,里面包含很多段(section),其中.text 段存放了运行代码,.data 段里面存放了已经初始化了的全局变量和静态局部变量,.rodata 段存放了程序中所有的常量等等,除了这些程序运行时需要用得到的代码和数据之外,还有一些是程序在 从磁盘加载到内存时需要提供给加载器的辅助信息,比如提供代码重定位信息的.rel.text 段, ELF 格式文件中的符号表.symtab 段等,这些信息将会在程序加载完毕之后被丢弃,而不会 存在于程序运行的内存当中。将多个不同的可重定位 ELF 格式文件链接成一个可执行 ELF 格式文件的过程中,会将它们不同的各个段按照“执行视图”合并起来,简言之,就是将具有相同权限的段合并到一起,比如各个文件中的具有只读权限的.text 段和.rodata 段将会被合并到一起,当程序有多个执行实例(多个进程)时,这些 执行实例会共享一个只读段的副本,从而节省内存空间。
也就是在经过连接过后,我们就可以通过可执行文件实现我们的设计了。
执行文件:
这就是一个C程序从构思到实现的一个简单实现过程,其中我们创建了几个文件:
由此我们可以总结一下:
.h文件就是头文件,里面包含的几乎是预处理要处理的信息。
.c文件就是我们写的C语言的结构体,函数等。
.i文件是执行预处理之后的文件。
.s文件是翻译之后的汇编语言文件。
.o文件是会变后的可重定位指令文件。
.exe文件就是最终的可执行文件。