C语言编译过程
GCC编译C源码有四个步骤:
预处理-----> 编译 ----> 汇编 ----> 链接
一、 编译和链接的流程
C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的库文件进行组织形成最终生成可执行代码的过程。过程图解如下:
从图上可以看到,整个代码的编译过程分为编译和链接两个过程,编译对应图中的大括号括起的部分,其余则为链接过程。
下面是对应的GNU工具链生成文件的过程:
说明:这些后缀并不是必须的,这只是常见的后缀方式,对于C++,对应一般为.cpp,.ii,.s,.o,exec/.so等。参考http://zhidao.baidu.com/question/89958523.html。当然,本质上,.c/.i/.s文件都是文本文件,可以直接查看内容的。.o/exec/.so等文件需要工具查看一些信息。
二、. 编译的三个阶段:
1. 预处理阶段
在正式的编译阶段之前进行,预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。预处理处理的主要内容包括:
A、宏定义,如#define PI 3.14
简单来说就是进行宏替换。
B、条件编译,如#ifdef,#ifndef,#else,#endif等等
预编译程序根据这些指令,将不必要参与编译的代码过滤掉。
C、头文件包含,如#include <filename.h>, #include "filename.h"
D、特殊符号,预编译程序可以识别一些特殊的符号
典型的就是__LINE__、__FILE__等编译器内置的预定义宏了。
总之,预处理的核心工作就是“替换”。当然,预处理也会去掉代码中的注释内容,总之,预处理的目的就是简化编译阶段扫描的内容。
对应的GCC选项:-E(预处理,但不编译),输出为对.c文件预处理后的结果,默认输出到控制台,使用-o指定输出到文件。对于GNU工具链,其提供了独立的预处理器,为cpp。
举例如下:
// File: test.h
#define MACRO_A 1
int foo();
// File: test.cpp
//#include <stdio.h>
#include "test.h" #define MACRO_B "macro_B" int main()
{
printf("MACRO_A is :%d; MACRO_B is: %s\n", foo(), MACRO_B);
return 1;
} int foo()
{
return MACRO_A;
}
编译:
gcc test.cpp -E -o out.ii
说明:这里的test.cpp中,注释了#include <stdio.h>进行预处理是不会报错的,只有在编译的时候才会提示printf错误。下面是输出的out.ii的内容:
# 1 "test.cpp"
# 1 "<built-in>"
# 1 "<命令行>"
# 1 "test.cpp" # 1 "test.h" 1 int foo();
# 4 "test.cpp" 2 int main()
{
printf("MACRO_A is :%d; MACRO_B is: %s\n", foo(), "macro_B");
return 1;
} int foo()
{
return 1;
}
说明:这里可以看到,里面有一些#开头的信息,这些信息在编译阶段是会忽略(不会被扫描)的,这些行的内容是一些记录一些行信息和文件的信息等等。之所以上面把#include <stdio.h>注释掉,是因为加入这一句之后,stdio.h中的内容也会被替换到out.ii中,这样内容太多,不适合贴在这里。
如果使用cpp进行预处理,那么就是(GCC内部还是调用cpp处理的):
cpp test.cpp -o out.ii
2. 编译和优化
经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main,if,else,for,while,{,}, +,-,*,\等等。
编译就是指传统的编译原理中提到的内容了,词法分析、语法分析、中间代码生成、汇编代码生成等。(说明:这里的中间代码生成是编译原理中的中间表达式的代码,不是.o目标文件,好像有时候也把目标文件称之为中间文件,这里区分一下)
优化也是编译原理中涉及的内容,优化涉及的内容很多,优化可以是对中间代码本身的优化或者目标代码的生成进行(即由中间代码生成汇编代码的过程或者生成目标文件的过程),关于优化,这里不深入探讨。
总之,这里说的编译的过程,就是由预处理的文件,编译优化得到汇编文件的过程。
对应的GCC选项:-S(编译,但不汇编)。输出为.s文件。说明:gcc编译可以以源文件作为输入,其实也是可以直接用预处理的.i文件作为输入的。当然,唯一要注意的是如果原来的源文件为cpp而输出是.i不是.ii,那么.i作为输入的时候,最好使用g++,否则可能编译有问题(gcc/g++会根据文件后缀判断是c还是c++的文件,所以对应就好了)。
对于GNU工具链,其提供了独立的预编译器,为ccl(好像没有这个命令呢?总之,ccp和ccl几乎不会用到,直接用gcc就可以了)。
还是上面的例子(把#include<stdio.h>取消注释):
$ gcc test.cpp -E -o test.ii
$ gcc test.cpp -S
$ gcc test.ii -S
$
说明:其中-S默认输出到文件中,所以不使用-o也是可以输出到文件中的。
3. 汇编
汇编实际上指把汇编语言代码翻译成目标机器指令的过程。其输出就是目标文件了(.o)。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。通常一个目标文件中至少有两个段:
代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。
说明:汇编之后,编译的过程就完成了,得到了目标文件(.o)。
对应的GCC选项:-c(汇编,但不链接)。
还是上面的例子(用.ii/.s/.cpp作为输入都是可以的,gcc会根据后缀知道输入是什么文件,从而在此基础上继续处理):
$ gcc test.ii -c
$ gcc test.s -c
$ gcc test.cpp -c
$
其中gcc test.s -c,就是直接编译汇编文件,而gcc test.cpp -c就会从预处理开始进行处理了。对于GNU工具链,其提供了独立的汇编器,为as。也可以使用as编译汇编文件(当然,也只能编译汇编文件了),上面的gcc test.s -c相当于:
$ as test.s -o test.o
(不使用-o test.o,那么输出默认为a.o)
总结:编译主要包括预处理、编译、汇编三个阶段,通过GCC的选项能控制GCC处理到某一步之后就停止,当然,实际的GCC处理,可能不一定是完全的一步一步的处理的,可能会有一些优化的处理方式。
三、链接
由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。
例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。
(1)静态链接
在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。
(2) 动态链接
在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。
对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。
四、整体过程
下面是整体的过程:
解释型语言:
C语言编译过程的更多相关文章
- C语言编译过程及数据类型
写在前面 C语言可以称得上是高级语言中的低级语言,接下来一段时间,我会写一下文章关于c语言,把它的神秘面纱一 一揭开.下面主要是c语言的C语言编译过程及数据类型 源文件编译过程 为了使计算机能执行高级 ...
- C语言编译过程以及gcc编译参数
1.1 C语言编译过程,gcc参数简介 1.1.1 C语言编译过程 一.gcc - o a a.c -o:指定文件输出名字 二.C语言编译的过程: 1.1.1 ...
- 转 C语言编译过程简介
C语言编译过程简介 C语言编译过程简介 刚开始接触编程的时候,只知道照书敲敲代码,一直都不知道为什么在windows平台下代码经过鼠标那样点击几下,程序的结果就会在那个黑色的屏幕上.现在找了个机会将C ...
- GCC 使用-C语言编译过程
任何一种高级语言,要想在机器上执行,必须翻译为机器能读懂的机器语言.编译器就相当于翻译官,将高级语言翻译为机器语言. GCC 最初只用了编译 C 语言程序,全称是 GNU C Compiler.后来扩 ...
- Go 语言编译过程
走进Golang之编译器原理_大愚Talk-CSDN博客 https://blog.csdn.net/hel12he/article/details/103061921 go编译器 - 知乎 http ...
- go语言编译过程概述
go语言编译过程概述 总结自<go语言设计与实现> 名词解释: 中间代码 中间代码是编译器或者虚拟机使用的语言,它可以来帮助我们分析计算机程序.在编译过程中,编译器会在将源代码转换到机器码 ...
- C语言编译过程详解
前言 C语言程序从源代码到二进制行程序都经历了那些过程?本文以Linux下C语言的编译过程为例,讲解C语言程序的编译过程. 编写hello world C程序: // hello.c #include ...
- C语言编译过程(转)
内容摘要 : C语言编译的整个过程是非常复杂的,里面涉及到的编译器知识.硬件知识.工具链知识都是非常多的,深入了解整个编译过程对工程师理解应用程序的编写是有很大帮助的,希望大家可以多了解一些,在遇到问 ...
- C语言基础(21)-C语言编译过程及GCC参数简介
任何C语言的编译过程可分为以下三部分: 一.预编译 在C语言中,以#开头的语句又叫预编译指令.预编译主要做以下两件事情: 1.将#include包含的头文件做简单的文本替换: 2.将代码中的注释删除. ...
随机推荐
- 谈谈final的作用
前言 一直想写写这个话题.代表公司也面试过一些求职者,每次面试我必问的两个问题之一就是“请你谈一谈对于final关键字的理解”.这是一个简单的小问题,但是不要小看它,通过对这个问题的回答以及一些简单的 ...
- git rm–r folder fatal:pathspec "" did not match any files
问题描述: 某年某月某日,在查看git库的时候,发现文件的分布和文件夹的名字是极其不合理的,所以移动和重命名了某些文件. 在删除(git rm –r folder)一个空文件夹的时候,出现错误:fat ...
- shell 中命令输入的快!捷!键!
非常棒!! 非常棒!! 删除ctrl + d 删除光标所在位置上的字符相当于VIM里x或者dlctrl + h 删除光标所在位置前的字符相当于VIM里hx或者dhctrl + k 删除光标后面所有字符 ...
- ssc
接了一个ssc的小项目,却因为对方的不作答而半途而废.我写了一天的代码算是废了. 主程序 <?xml version="1.0" encoding="utf-8&q ...
- iOS ARC模式 内存管理
1,测试一 ;i<;i++) { NSLog(@"i = %d",i); } 2,测试二 ;i<;i++) { NSLog(@"i = %d",i ...
- EF架构~扩展一个分页处理大数据的方法
回到目录 最近总遇到大数据的问题,一次性处理几千万数据不实际,所以,我们需要对大数据进行分块处理,或者叫分页处理,我在EF架构里曾经写过类似的,那是在进行BulkInsert时,对大数据批量插入时候用 ...
- 在Windows下安装Memcached
Windows下的Memcache安装: 需要运行命令行工具cmd 请以管理员权限运行 开始->附件->命令提示符,以管理员身份运行 假如当前C:\windows\system32,输入c ...
- Node.js入门:事件机制
Evented I/O for V8 JavaScript 基于V8引擎实现的事件驱动IO. 事件机制的实现 Node.js中大部分的模块,都继承自Event模块(http://n ...
- Atitit 数据存储的数据表连接attilax总结
Atitit 数据存储的数据表连接attilax总结 1.1. 三种物理连接运算符:嵌套循环连接.合并连接以及哈希连接1 1.2. a.嵌套循环连接(nested loops join)1 1.3. ...
- salesforce 零基础学习(三十八)Translate 的使用(国际化处理)
本篇参考:http://resources.docs.salesforce.com/200/17/en-us/sfdc/pdf/salesforce_workbench_cheatsheet.pdf ...