C语言真正的编译过程

说实话,很多人做了很久的C/C++,也用了很多IDE,但是对于可执行程序的底层生成一片茫然,这无疑是一种悲哀,可以想象到大公司面试正好被问到这样的问题,有多悲催不言而喻,这里正由于换工作的缘故,所以打算系统的把之前用到的C/C++补一补。这里权且当做抛砖引玉,大神飘过。

【总述】

从一个源文件(.c)到可执行程序到底经历了哪几步,我想大多数的人都知道,到时到底每一步都做了什么,我估计也没多少人能够说得清清楚楚,明明白白。

其实总的流程是这样的。

【第一步】编辑hello.c

1 #include <stdio.h>
2 #include <stdlib.h>
3 int main()
4 {
5         printf("hello world!\n");
6         return 0;
7 }

【第二步】预处理

预处理过程实质上是处理“#”,将#include包含的头文件直接拷贝到hell.c当中;将#define定义的宏进行替换,同时将代码中没用的注释部分删除等

具体做的事儿如下:

(1)将所有的#define删除,并且展开所有的宏定义。说白了就是字符替换

(2)处理所有的条件编译指令,#ifdef #ifndef #endif等,就是带#的那些

(3)处理#include,将#include指向的文件插入到该行处

(4)删除所有注释

(5)添加行号和文件标示,这样的在调试和编译出错的时候才知道是是哪个文件的哪一行

(6)保留#pragma编译器指令,因为编译器需要使用它们。

gcc -E hello.c -o a.c可以生成预处理后的文件。通过查看文件内容和文件大小可以得知a.c讲stdio.h和stdlib.h包含了进来。

【第三步】编译

编译的过程实质上是把高级语言翻译成机器语言的过程,即对a.c做了这些事儿

(1)词法分析,

(2)语法分析

(3)语义分析

(4)优化后生成相应的汇编代码

从 高级语言->汇编语言->机器语言(二进制)

gcc -S hello.c -o a.s可以生成汇编代码

汇编代码如下。

 1         .file   "hello.c"
 2         .section        .rodata
 3 .LC0:
 4         .string "hello world!"
 5         .text
 6         .globl  main
 7         .type   main, @function
 8 main:
 9 .LFB0:
10         .cfi_startproc
11         pushl   %ebp
12         .cfi_def_cfa_offset 8
13         .cfi_offset 5, -8
14         movl    %esp, %ebp
15         .cfi_def_cfa_register 5
16         andl    $-16, %esp
17         subl    $16, %esp
18         movl    $.LC0, (%esp)
19         call    puts
20         movl    $0, %eax
21         leave
22         .cfi_restore 5
23         .cfi_def_cfa 4, 4
24         ret
25         .cfi_endproc
26 .LFE0:
27         .size   main, .-main
28         .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
29         .section        .note.GNU-stack,"",@progbits

gcc -c hello.c -o a.o将源文件翻译成二进制文件。类Uinx系统编译的结果生生成.o文件,Windows系统是生成.obj文件。

编译的过程就是把hello.c翻译成二进制文件

【第四步】链接

就像刚才的hello.c它使用到了C标准库的东西“printf”,但是编译过程只是把源文件翻译成二进制而已,这个二进制还不能直接执行,这个时候就需要做一个动作,

将翻译成的二进制与需要用到库绑定在一块。打个比方编译的过程就向你对你老婆说,我要吃雪糕。你只是给你老婆发出了你要吃雪糕的诉求而已,但是雪糕还没有到。

绑定就是说你要吃的雪糕你的老婆已经给你买了,你可以happy。

gcc hello.c -o a可以生成可执行程序。即gcc不带任何参数。ldd就可以看到你的可执行程序依赖的库。

可以看到a.o的大小是1.1k,毕竟他只是把源文件翻译成二进制文件。a却有7k,应该是他多了很多“绳子”吧。在运行的时候这些“绳子”就将对应的库函数“牵过来”。很形象的比喻是不是?哈哈。libc.so.6 中就对咱们用的printf进行了定义。

这就是编写的整个流程,(⊙o⊙)。谢谢各位看官。不足的地方请不吝赐教。

时间: 2024-10-11 03:25:13

C语言真正的编译过程的相关文章

C语言学习笔记---001C语言的介绍,编译过程原理,工具等

技术qq交流群:Dream:251572072 ----------------------------------------------------------------------- a.C语言的历史 起源 标准化 b.基于c的语言 编程语言都借鉴了c语言的特性 c++语言:包含所有c语言特性,增加了类和其他的特性支持面向对象编程 java:基于C++的,包含了一些c的特性 C#:是由java和c++发展起来的 Perl:脚本语言 c.c语言特性: c语言是一种底层语言,为了适应系统编程

Linux下C语言程序的编译过程与ssc平台出租

使用gcc编译程序时,编译工程分为4个阶段:ssc平台出租(企 娥:217 1793 408) (1)预处理:(Pre-Processing) (2)编译:(Compiling) (3)汇编:(Assembling) (4)链接:(linking) 预处理.编译.汇编是3个不同的阶段,但gcc在实际操作时可以把3个步骤合并为一个步骤来执行.下面以一个实例介绍如何生成各个阶段的代码.下面演示都针对文件 hello.c 进行. 1.编译单个文件 首先 vim hello.c 在hello.c中 按下

C语言的编译过程、安装gcc编译器以及设置环境变量

以我对C语言编译过程的了解,我用了一点时间画了一个图,提供给大家参考一下,希望有些能对您的问题提上帮助. 前几天刚初步学习了C语言的编译过程,感触挺深的.在C语言中头文件其实起了一个很大的作用. 1.头文件可以不需要编译 2.可以查看具体的声明 3.头文件加上实现文件的o文件提交给使用者即可 ,不需要知道源代码 4..o文件预先编译,所以整个项目编译时,会大大提高编译的时间 . 5.当一个文件(A.c文件)依赖于头文件(b.h)时 ,如果b.c编译之后形成的b.o文件重新编译后,a.o的文件不需

C语言程序编译过程

最近在编译DM8168的ARM端程序时经常出现未定义.重定义等报错,由于源码文件多,包含关系比较多,所以自己添加时容易乱.深深的体会到,好的代码风格是如此重要,之前也在看代码重构,以后应该更加注意代码的质量.经思考总结规律如下: 1.公用的数据结构等写为一个头文件,其他源文件包含此头文件.同时为了让不同源文件里的函数都可以使用,公用的函数可以放在此头文件中声明. 2.其他源文件里声明的变量,如果想在另一个文件里用,需要extern声明,这样可以避免各种全局变量的交互混杂. 理解的比较浅,希望高人

李洪强iOS开发之C语言程序编译过程

汇编语言 指令用特定的名字来标记,这就是汇编语言 人比较容易看懂汇编语言 汇编直接和程序一一对应的 有汇编器把程序翻译成机器码 把高级语言编译成计算机识别的语言 程序编译过程 命令行 UNIX 系统中自带了C语言的编译器,编译器的名字叫CC CC 的含义是C Compler Linux系统是一个开源的,它自带的C编译器叫GCC GCC 不仅可以编译C 还可以编译Python 和OC MAC OS 的编译器是苹果公司自己研发的 Clang 在用CC或者GCC 的命令的时候,本质是调用了clang

C语言link过程详解(多文件编译过程)

原文摘自:http://www.cppblog.com/shifan3/archive/2007/01/05/17325.html详解link有些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错误信息不能定位到某一行).或者对语言的一些部分不知道为什么要(或者不要)这样那样设计.了解本文之后,或许会有一些答案.首先看看我们是如何写一个程序的.如果你在使用某种IDE(V

C语言-第15课 - 编译过程简介

第15课  - 编译过程简介 1. 编译器做了什么 (1)预编译: l 处理所有的注释,以空格代替. l 将所有的#define删除,并且展开所有的宏定义. l 处理条件编译指令#if,#ifdef,#elif,#else,#endif. l 处理#include,展开被包含的文件. l 保留编译器需要使用的#pragma指令. 预处理指令:gcc -E file.c -o hello.i (2)编译 l 对预处理的文件进行一系列的词法分析,语法分析和语义分析: 词法分析主要分析关键字,标示符,

GCC与编译过程

GCC与编译过程   GCC(GNU Compiler Colletion),GUN编译器套装,是一套由GNU开发的编程语言编译器.Linux系统下的GCC编译器实际上是调用其他不同的工具来完成预处理.编译.汇编和链接工作. 一.编译过程 在计算机的眼里,只有1和0.不幸的是,我们用C语言写出来的代码,计算机无法直接看明白.所以一个程序如果需要被计算机执行,那么就必须翻译成能被计算机读懂并执行的1和0.实现这一结果的过程,我们称之为编译. 编译包括以下步骤:预处理.编译.汇编和链接.具体过程如下

Hive SQL的编译过程

Hive是基于Hadoop的一个数据仓库系统,在各大公司都有广泛的应用.美团数据仓库也是基于Hive搭建,每天执行近万次的Hive ETL计算流程,负责每天数百GB的数据存储和分析.Hive的稳定性和性能对我们的数据分析非常关键. 在几次升级Hive的过程中,我们遇到了一些大大小小的问题.通过向社区的咨询和自己的努力,在解决这些问题的同时我们对Hive将SQL编译为MapReduce的过程有了比较深入的理解.对这一过程的理解不仅帮助我们解决了一些Hive的bug,也有利于我们优化Hive SQL