﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-soak-随笔分类-C/C++</title><link>http://www.cppblog.com/soak/category/18887.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 21 Mar 2012 10:49:22 GMT</lastBuildDate><pubDate>Wed, 21 Mar 2012 10:49:22 GMT</pubDate><ttl>60</ttl><item><title>C程序编译过程浅析</title><link>http://www.cppblog.com/soak/archive/2012/03/21/168517.html</link><dc:creator>cpp</dc:creator><author>cpp</author><pubDate>Wed, 21 Mar 2012 08:52:00 GMT</pubDate><guid>http://www.cppblog.com/soak/archive/2012/03/21/168517.html</guid><wfw:comment>http://www.cppblog.com/soak/comments/168517.html</wfw:comment><comments>http://www.cppblog.com/soak/archive/2012/03/21/168517.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/soak/comments/commentRss/168517.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/soak/services/trackbacks/168517.html</trackback:ping><description><![CDATA[<div><p>前几天看了《程序员的自我修养&#8212;&#8212;链接、装载与库》中的第二章&#8220;编译和链接&#8221;，主要根据其中的内容简单总结一下C程序编译的过程吧。<br /><br /> </p><p>我现在一般都是用gcc，所以自然以GCC编译hellworld为例，简单总结如下。<br /></p> <p>hello.c源代码如下：</p><div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><span style="color: #000000;">&nbsp;#include&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">stdio.h</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;main()<br />&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(&#8220;Hello,&nbsp;world.\n&#8221;);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span>&nbsp;<span style="color: #000000;">0</span><span style="color: #000000;">;<br />&nbsp;}</span></div><p>通常我们使用gcc来生成可执行程序，命令为：gcc hello.c，默认生成可执行文件a.out</p> <p><br />其实编译（包括链接）的命令：gcc hello.c 可分解为如下4个大的步骤：</p> <ul><li>预处理(Preprocessing)</li><li>编译(Compilation)</li><li>汇编(Assembly)</li><li>链接(Linking)</li></ul><img alt="" src="http://www.cppblog.com/images/cppblog_com/soak/gcc_compilation.png" /><br /><p><br /></p><p>1. 预处理(Preproceessing)<br /><br />预处理的过程主要处理包括以下过程：<br />将所有的#define删除，并且展开所有的宏定义<br />处理所有的条件预编译指令，比如#if #ifdef #elif #else #endif等<br />处理#include 预编译指令，将被包含的文件插入到该预编译指令的位置。<br />删除所有注释 &#8220;//&#8221;和&#8221;/* */&#8221;.<br />添加行号和文件标识，以便编译时产生调试用的行号及编译错误警告行号。<br />保留所有的#pragma编译器指令，因为编译器需要使用它们<br /><br /><br /><br />通常使用以下命令来进行预处理：<br /><br />gcc -E hello.c -o hello.i<br /><br />参数-E表示只进行预处理 或者也可以使用以下指令完成预处理过程<br /><br />cpp hello.c &gt; hello.i /* cpp &#8211; The C Preprocessor */<br /><br />直接cat hello.i 你就可以看到预处理后的代码<br /><br /><br /><br />2. 编译(Compilation)<br /><br />编译过程就是把预处理完的文件进行一系列的词法分析，语法分析，语义分析及优化后生成相应的汇编代码。<br /><br />$gcc &#8211;S hello.i &#8211;o hello.s<br /><br />或者<br /><br />$ /usr/lib/gcc/i486-linux-gnu/4.4/cc1 hello.c<br /><br />注：现在版本的GCC把预处理和编译两个步骤合成一个步骤，用cc1工具来完成。gcc其实是后台程序的一些包装，根据不同参数去调用其他的实际处理程序，比如：预编译编译程序cc1、汇编器as、连接器ld<br /><br />可以看到编译后的汇编代码(hello.s)如下：</p><div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><span style="color: #000000;">.file&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">hello.c</span><span style="color: #000000;">"</span><span style="color: #000000;"><br />.section&nbsp;.rodata<br />.LC0:<br />.</span><span style="color: #0000ff;">string</span>&nbsp;<span style="color: #000000;">"</span><span style="color: #000000;">Hello,&nbsp;world.</span><span style="color: #000000;">"</span><span style="color: #000000;"><br />.text<br />.globl&nbsp;main<br />.type&nbsp;main,&nbsp;@function<br />main:<br />pushl&nbsp;</span><span style="color: #000000;">%</span><span style="color: #000000;">ebp<br />movl&nbsp;</span><span style="color: #000000;">%</span><span style="color: #000000;">esp,&nbsp;</span><span style="color: #000000;">%</span><span style="color: #000000;">ebp<br />andl&nbsp;$</span><span style="color: #000000;">-</span><span style="color: #000000;">16</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">%</span><span style="color: #000000;">esp<br />subl&nbsp;$</span><span style="color: #000000;">16</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">%</span><span style="color: #000000;">esp<br />movl&nbsp;$.LC0,&nbsp;(</span><span style="color: #000000;">%</span><span style="color: #000000;">esp)<br />call&nbsp;puts<br />movl&nbsp;$</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">%</span><span style="color: #000000;">eax<br />leave<br />ret<br />.size&nbsp;main,&nbsp;.</span><span style="color: #000000;">-</span><span style="color: #000000;">main<br />.ident&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">GCC:&nbsp;(Ubuntu&nbsp;4.4.3-4ubuntu5)&nbsp;4.4.3</span><span style="color: #000000;">"</span><span style="color: #000000;"><br />.section&nbsp;.note.GNU</span><span style="color: #000000;">-</span><span style="color: #000000;">stack,</span><span style="color: #000000;">""</span><span style="color: #000000;">,@progbits</span></div><div></div><div>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 汇编(Assembly)</div> <div>汇编器是将汇编代码转变成机器可以执行的命令，每一个汇编语句几乎都对应一条机器指令。汇编相对于编译过程比较简单，根据汇编指令和机器指令的对照表一一翻译即可。</div> <div>$ gcc &#8211;c hello.c &#8211;o hello.o</div> <div>或者</div> <div>$ as hello.s &#8211;o hello.co</div> <div>由于hello.o的内容为机器码，不能以普通文本形式的查看（vi 打开看到的是乱码）。</div> <div>&nbsp;</div> <div>4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 链接(Linking)</div> <div>通过调用链接器ld来链接程序运行需要的一大堆目标文件，以及所依赖的其它库文件，最后生成可执行文件。</div> <div>ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.o (省略了文件的路径名)。</div> <div>&nbsp;</div> <div>helloworld的大体编译和链接过程就是这样了，那么编译器和链接器到底做了什么呢？</div> <div>&nbsp;</div> <div>编译过程可分为6步：扫描（词法分析）、语法分析、语义分析、源代码优化、代码生成、目标代码优化。</div> <div>词法分析：扫描器（Scanner）将源代的字符序列分割成一系列的记号（Token）。lex工具可实现词法扫描。</div> <div>语法分析：语法分析器将记号（Token）产生语法树（Syntax Tree）。yacc工具可实现语法分析(yacc: Yet Another Compiler Compiler)。</div> <div>语义分析：静态语义（在编译器可以确定的语义）、动态语义（只能在运行期才能确定的语义）。</div> <div>源代码优化：源代码优化器(Source Code Optimizer)，将整个语法书转化为中间代码（Intermediate Code）（中间代码是与目标机器和运行环境无关的）。中间代码使得编译器被分为前端和后端。编译器前端负责产生机器无关的中间代码；编译器后端将中间代 码转化为目标机器代码。</div> <div>目标代码生成：代码生成器(Code Generator).</div> <div>目标代码优化：目标代码优化器(Target Code Optimizer)。</div> <div>&nbsp;</div> <div>链接的主要内容是把各个模块之间相互引用的部分处理好，使得各个模块之间能够正确地衔接。</div> <div>链接的主要过程包括：地址和空间分配（Address and Storage Allocation），符号决议（Symbol Resolution），重定位（Relocation）等。</div> <div>链接分为静态链接和动态链接。</div> <div>静态链接是指在编译阶段直接把静态库加入到可执行文件中去，这样可执行文件会比较大。</div> <div>而动态链接则是指链接阶段仅仅只加入一些描述信息，而程序执行时再从系统中把相应动态库加载到内存中去。</div> <div>静态链接的大致过程如下图所示：<br /><img alt="" src="http://www.cppblog.com/images/cppblog_com/soak/static_linking.png" /></div><p><br /></p><p><br /></p><p><br /></p></div><img src ="http://www.cppblog.com/soak/aggbug/168517.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/soak/" target="_blank">cpp</a> 2012-03-21 16:52 <a href="http://www.cppblog.com/soak/archive/2012/03/21/168517.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>