一直用windows一键搞定, 没有去了解详细的编译链接过程, 今天看了一篇文章, 顺便实验和记录在Linux下逐步生成的步骤.

预处理: 执行#include, #define, #if, #ifdef等预处理指令 把宏展开

编译: 把源文件编译为汇编语言文件 对所有常量表达式(只包含常量的表达式)求值发生在此阶段(不是预处理阶段)

汇编: 把汇编语言文件翻译称为机器语言指令

链接: 连接器就负责处理合并各种用到的*.o, 比如用到的printf函数就会连接printf.o, 结果就得到一个可执行文件, 可以被加载到内存中由系统执行

先说下gcc常用选项

  --version: 查看gcc版本号及版权信息

  -x language: 指明使用的编程语言, 允许的语言包括c、c++、assembler none,  ‘none’意味着恢复默认行为, 即根据文件的扩展名猜测源文件的语言

  -o: *输出到指定文件 (与其他选项配合生成指定步骤下的文件)

  -E: *仅做预处理, 不进行编译、汇编和链接, 即执行#include, #define, #if, #ifdef等预处理指令

  -S: *仅编译到汇编语言, 不进行汇编和链接, 即把源文件翻译为汇编语言

  -c: *编译、汇编到目标代码(目标代码可不是执行文件), 不进行链接, 从-E、-S到-c命令执行的步骤范围从小到大

  -pipe: 使用管道代替临时文件

  -combine: 将多个源文件一次性传递给汇编器

  **如果不指定参数则自动执行预处理、编译到汇编语言、汇编到目标代码、链接生成可执行文件**

  

  -l library或者-llibrary: 进行链接时搜索名为library的库, gcc hello.c -lm -o hello

  -Idir: 把dir加入到搜索头文件的路径列表中, gcc hello.c -I../inc -o hello

  -Ldir: 把dir加入到搜索库文件的路径列表中, gcc -I/home/foo -L/home/foo -ltest test.c -o test

  -g: 表示在生成的目标文件中带调试信息, 调试信息可以在程序异常中止产生core后, 帮助分析错误产生的源头, 包括产生错误的文件名和行号等非常多有用的信息

  -Wall: 会打开一些很有用的警告选项, 建议编译时加此选项

  -w: 禁止显示所有警告信息

先来个hello world

 #include <stdio.h>
main() {
printf("Hello World!\n");
}

将上面内容保存为hello.c, 并通过gcc去编译它

  gcc -g -Wall hello.c -o hello

执行完上述语句后程序会报错

  

  hello.c:3: warning: return type defaults to ‘int’ /*当函数没有设置返回值类型的时候, C语言默认程序返回的是int类型*/

  hello.c:5: warning: control reaches end of non-void function /*警告: 在有返回值的函数中, 控制流程到达函数尾 [-Wreturn-type]*/

解决上述问题的方法很简单, 代码如下

 #include <stdio.h>
int main() {
printf("Hello World!\n");
return ;
}

再次执行gcc -g -Wall hello.c -o hello

  

下面就来详细讲解整个编译过程

  

上图是一个hello的c程序由gcc编译器从源码文件hello.c中读取内容并将其翻译成为一个可执行的对象文件hello的过程, 这个过程包含了几个阶段:

  1.预处理过程

    预处理根据以字符#开头的命令修改原始的C程序, 比如hello.c中的第一行的#include <stdio.h> 命令告诉预处理器读取系统头文件stdio.h的内容, 插入到程序文本中, 结果就得到里另一个C程序, 通常是以*.i作为文件扩展名

    可通过如下指令获得gcc -E hello.c -o hello.i

    

    用记事本打开hello.i可以看到如下代码(注意行数)

    

    

    可以看到除了#include <stdio>指令之外, 其他指令均未被改变

  2.编译阶段(即把源文件转为汇编语言): 编译器将文本文件hello.i翻译成文本文件hello.s, 它包含一个汇编语言程序, 汇编语言程序中的每条语句都以一种标准的文件格式确切地描述了一条低级机器语言指令

    可通过如下指令获得: gcc -S hello.c -o hello.s

    打开hello.s可以看到

    

  3.汇编阶段(把汇编代码翻译称为机器语言指令): 汇编器将hello.s翻译成机器语言指令, 把这些指令打包成一种可重定位目标程序的格式, 并把结果保存在hello.o中, hello.o是一个二进制文件, 它的字节编码是机器语言指令, 而不是字符

    可通过如下指令获得: gcc -c hello.c, 最终生成的hello.o文件需要使用objdump打开, 具体指令为: objdump -d hello.o, 所查看到的内容为

    

  4.链接阶段: hello.c程序中调用了printf函数, 而printf函数存在于一个名为printf.o的单独的预编译好的目标文件中, 而这个文件必须以某种方式合并到我们的hello.o程序中, 连接器就负责处理这种合并, 结果就得到hello文件, 它是一个可执行文件, 可以被加载到内存中由系统执行

    使用的指令为: gcc hello.o -o hello, 最终生成了一个hello文件, 同样此hello文件可用通过objdump打开: objdump -d hello

经过上面四个过程, 我们就可以把一个源代码文件编译成机器能运行的可执行文件, 这个可执行文件刚开始是保存在磁盘上, 当计算机要运行这个程序的时候, hello就被加载到内存中, 接着程序指令被不断复制到寄存器中由CPU来执行, 最后把”hello world”从寄存器中打到显示设备上, 这就是hello程序整个执行过程

参考: http://blogread.cn/it/article/6492?f=wb1

C-从源文件到可执行文件的详细编译链接过程的更多相关文章

  1. [转]C++编译链接过程详解

    C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接.编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程.链接是把目标文件.操作 ...

  2. 转:C语言的编译链接过程的介绍

    11:42:30 C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接.编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程.链接 ...

  3. GCC编译链接过程

    编译链接过程 代码 #cat main.c #include <stdio.h> int add(int x, int y); int sub(int x, int y); int mul ...

  4. C/C++编译链接过程详解

    有些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错误信息不能定位到某 ...

  5. 转:从编译链接过程解析static函数的用法

    关于static函数的用法 就像我们熟知的那样,变量可以分全局的和局部的,函数也可以分全局的和局部的. 比如说,在一个工程的common.h中定义了一个全局变量 int test;那么在整个工程的作用 ...

  6. 【对象模型】C++模版的编译链接过程——编译器真的会检查所有tocken层面的错误么?

    模版(template)设计的初衷,是设计一种自动实例化机制,不需要使用者参与,编译器可根据使用者提供的模版参数再套用类的定义来实例化.所谓实例化,除了包含对于程序变量的实例化,即开辟空间并设置某些变 ...

  7. Delphi编译/链接过程

    下面展示了Delphi是怎样编译源文件,并且把它们链接起来,最终形成可执行文件. 当Delphi编译项目(Project)时,将编译项目源文件.窗体单元和其他相关单元,在这个过程中将会发生好几件事情: ...

  8. Delphi 编译/链接过程

     

  9. OC-01 编译链接的作用

    编译:检测代码的语法合法性,随后生成.o文件. 链接:把项目中所有的.out合并,生成一个可执行文件. OC编译连接过程 .m---->.o---->.out  . 检测源文件的语法合法性 ...

随机推荐

  1. 如何利用javascript获取表单中select下拉列表中所选中项的值value

    1.html代码如下: <html> <head> </head> <body> <form name="form1" id= ...

  2. Serializable java序列化

    Bean Serializable Interface 的接口让BEAN可以串行化,将其变成一个可保存为以后使用的二进制流.当一个BEAN被系列化到磁盘上或者其他任何地方,其状态被保存起来,其中的属性 ...

  3. Oracle中Instr用法

    在项目中用到了Oracle中 Instr 这个函数,顺便仔细的再次学习了一下这个知识. Oracle中,可以使用 Instr 函数对某个字符串进行判断,判断其是否含有指定的字符. 其语法为:Instr ...

  4. plsql调用无参/有参存储过程

    --有参调用方式:declare STERMINAL ); SPROCESS NUMBER; begin sj_transfera_digi_getmapping(,,'DYH010006783031 ...

  5. Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

    在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),G ...

  6. Guava Files 源码分析(一)

    Files中的工厂 Files类中对InputStream, OutputStream以及Reader,Writer的操作封装了抽象工厂模式,抽象工厂是InputSupplier与OutputSupp ...

  7. go语言基础之数组比较和赋值

    1.go语音基础之数组比较和赋值 示例: package main //必须有个main包 import "fmt" func main() { //支持比较,只支持 == 或 ! ...

  8. 总结div里面水平垂直居中的实现方法

    最近经常碰到要垂直居中的问题,所以想着总结一下:关于如何设置小盒子在大盒子里面水平垂直方向同时居中的实现方法有很多种,下面仅列举了常用的几种. 首先看一下要实现的效果图及对应的html代码: < ...

  9. Android之属性动画(二)

    上一篇文章(链接:http://www.cnblogs.com/jerehedu/p/4458928.html  ),我们对属性动画有了简单的认识,并实际动手使用ObjectAnimator.Anim ...

  10. Java Object Clone

    Java Object Clone User user = new User(); user.setName("tom"); User user1 = new User(); us ...