转自:http://www.360doc.com/content/14/0109/16/835125_343879650.shtml

  1. C/C++编译过程
  2. C/C++编译过程主要分为4个过程
  3. 1) 编译预处理
  4. 2) 编译、优化阶段
  5. 3) 汇编过程
  6. 4) 链接程序
  7. 一、编译预处理
  8. (1)宏定义指令,如#define Name TokenString,#undef等。 对于前一个伪指令,预编译所要做的是将程序中的所有Name用TokenString替换,
  9. 但作为字符串常量的 Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。
  10. (2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif等。 这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。
  11. 预编译程序将根据有关的文件,将那些不必要的代码过滤掉
  12. (3) 头文件包含指令,如#include "FileName"或者#include <FileName>等。 在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),
  13. 同时包含有各种外部符号的声明。 包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在/usr/include目录下。
  14. 在程序中#include它们要使用尖括号(< >)。
  15. 另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在#include中要用双引号("")。
  16. (4)特殊符号,预编译程序可以识别一些特殊的符号。 例如在源程序中出现的#line标识将被解释为当前行号(十进制数),
  17. 上面程序实现了对宏line的运用
  18. (5)预处理模块 预处理工作由#pragma命令完成,#Pragma命令将设定编译器的状态或者是指示编译器完成一些特定的动作。
  19. #pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。
  20. 依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
  21. 打开C标准库函数,如stdio.h,我们总能找到下面这一句指示编译器初始化堆栈
  22. #include "iostream"
  23. #line 100
  24. using namespace std;
  25. int main(int argc, char* argv[])
  26. {
  27. cout<<"__LINE__:"<<__LINE__<<endl;
  28. return 0;
  29. }
  30. /*--------------------
  31. * 输出结果为:
  32. * __LINE__:103
  33. * 本来输出的结果应该是 7,但是用#line指定行号之后,使下一行的行号变为,
  34. * 到输出语句恰为行103
  35. ---------------------*/
  36. C/C++编译过程
  37. 或者程序指示编译器去链接系统动态链接库或用户自定义链接库
  38. 二、编译、优化阶段
  39. 经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main,if,else,for,while,{,}, +,-,*,\等等。
  40. 在《编译原理》中我们可以了解到一个编译器对程序代码的编译主要分为下面几个过程:
  41. a) 词法分析
  42. b) 语法分析
  43. c) 语义分析
  44. d) 中间代码生成
  45. e) 代码优化
  46. f) 代码生成
  47. g) 符号表管理
  48. h) 将多个步骤组合成趟
  49. i) 编译器构造工具
  50. 在这里我们主要强调对函数压栈方式(函数调用约定)的编译处理
  51. C与C++语言调用方式大体相同,下面是几种常用的调用方式:
  52. __cdecl 是C DECLaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,
  53. 这些参数由调用者清除,称为手动清栈。被调用函数不需要求调用者传递多少参数,调用者传递过多或者过少的参数,
  54. 甚至完全不同的参数都不会产生编译阶段的错误。
  55. _stdcall 是StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,
  56. 最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除,使用的指令是 retnX,X表示参数占用的字节数,
  57. CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。函数在编译的时候就必须确定参数个数,
  58. 并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。
  59. PASCAL 是Pascal语言的函数调用方式,在早期的c/c++语言中使用这种调用方式,
  60. 参数压栈顺序与前两者相反,但现在我们在程序中见到的都是它的演化版本,其实
  61. #pragma comment(lib,_T("GDI32.lib"))
  62. #ifdef _MSC_VER
  63. /*
  64. * Currently, all MS C compilers for Win32 platforms default to 8 byte
  65. * alignment.
  66. */
  67. #pragma pack(push,_CRT_PACKING)
  68. #endif /* _MSC_VER */
  69. C/C++编译过程
  70. 质是另一种调用方式
  71. _fastcall是编译器指定的快速调用方式。由于大多数的函数参数个数很少,使用堆栈传递比较费时。因此_fastcall通常规定将前两个(或若干个)参数由寄存器传递,其余参数还是通过堆栈传递。不同编译器编译的程序规定的寄存器不同。返回方式和_stdcall相当。
  72. _thiscall 是为了解决类成员调用中this指针传递而规定的。_thiscall要求把this指针放在特定寄存器中,该寄存器由编译器决定。VC使用ecx,Borland的C++编译器使用eax。返回方式和_stdcall相当。
  73. _fastcall 和 _thiscall涉及的寄存器由编译器决定,因此不能用作跨编译器的接口。所以Windows上的COM对象接口都定义为_stdcall调用方式。
  74. C中不加说明默认函数为_cdecl方式(C中也只能用这种方式),C++也一样,但是默认的调用方式可以在IDE环境中设置。简单的我们可以从printf函数看出
  75. printf使用从从左至右压栈,返回int型并由_CRTIMP指定封在动态链接库中。
  76. 通过金典的hello world程序我们可以知道编译器对其argc和argv[]这两个参数进行了压栈,并且argc留在了栈顶
  77. 优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化处理主要分为下面几个过程:
  78. 1) 局部优化
  79. a) 基本块的划分
  80. b) 基本块的变换
  81. c) 基本块的DAG表示
  82. d) DAG的应用
  83. e) 构造算法讨论
  84. 2) 控制流分析和循环优化
  85. a) 程序流图与循环
  86. /*金典的hello world*/
  87. #include <stdio.h>
  88. int main(int argc, char* argv[])
  89. {
  90. printf("hello world");
  91. return 0;
  92. }
  93. _Check_return_opt_ _CRTIMP int __cdecl printf(_In_z_ _Printf_format_string_ const char * _Format, ...);
  94. #define CALLBACK _stdcall /* Windows程序回调函数*/
  95. #define WINAPI _stdcall
  96. #define WINAPIV _cdecl
  97. #define PASCAL _stdcall /*在c++语言中使用了StandardCall调用方式*/
  98. #define PASCAL _cdecl/*在c语言中使用了C DECLaration调用方式*/
  99. C/C++编译过程
  100. b) 循环
  101. c) 循环的查找
  102. d) 可归约流图
  103. e) 循环优化
  104. 3) 数据流的分析与全局优化
  105. a) 一些主要的概念
  106. b) 数据流方程的一般形式
  107. c) 到达一定值数据流方程
  108. d) 可用表达式及其数据流方程
  109. e) 活跃变量数据流方程
  110. f) 复写传播
  111. 经过优化得到的汇编代码必须经过汇编程序的汇编转换成相应的机器指令,方可能被机器执行。
  112. 三、汇编过程
  113. 汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,
  114. 都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。
  115. 目标文件由段组成。通常一个目标文件中至少有两个段: 代码段:该段中所包含的主要是程序的指令。
  116. 该段一般是可读和可执行的,但一般却不可写。 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。
  117. 四、链接程序
  118. 由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。
  119. 例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);
  120. 在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
  121. 链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,
  122. 使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。
  123. 根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:
  124. (1)静态链接 在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。
  125. 这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,
  126. 其中的每个文件含有库中的一个或者一组相关函数的代码。
  127. (2) 动态链接
  128. 在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。
  129. 链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量
  130. 的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应
  131. 进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。
  132. C/C++编译过程
  133. 对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动
  134. 态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一
  135. 些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一
  136. 定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。

【转载】C/C++编译过程分析的更多相关文章

  1. u-boot-2016.09 make编译过程分析(二)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/guyongqiangx/article/ ...

  2. u-boot-2016.09 make编译过程分析(一)

    https://blog.csdn.net/guyongqiangx/article/details/52565493 综述 u-boot自v2014.10版本开始引入KBuild系统,Makefil ...

  3. <转载>gcc/g++编译

    转载于:http://www.cnblogs.com/yc_sunniwell/archive/2010/07/22/1782678.html 1. gcc/g++在执行编译工作的时候,总共需要4步 ...

  4. Linux移植之make uImage编译过程分析

    编译出uboot可以运行的linux内核代码的命令是make uImage,下面详细介绍下生成linux-2.6.22.6/arch/arm/boot/uImage的过程: 1.vmlinux.Ima ...

  5. [转载]Linux内核编译

    原文地址:https://blog.csdn.net/qq_34247099/article/details/50949720 写在前面的话: 本人大二,东南大学一个软工狗,正在修一门名为<操作 ...

  6. (转载)JAVA动态编译--字节代码的操纵

    在一般的Java应用开发过程中,开发人员使用Java的方式比较简单.打开惯用的IDE,编写Java源代码,再利用IDE提供的功能直接运行Java 程序就可以了.这种开发模式背后的过程是:开发人员编写的 ...

  7. 转载:回编译APK出错:java.nio.char set.MalformedInputException: Input length = 1

    使用APKtool回编译APK,出现错误如下:    Exception in thread "main" org.yaml.snakeyaml.error.YAMLExcepti ...

  8. (转载)反编译android的apk文件步骤

    下面的方法我已经尝试过,完全可以成功,重点的步骤我在这里说一下 1.必须要有java环境,记得配置好环境变量 2.如果只查看class中的函数文件,只需要下载dex2jar和jd-gui 3.下载地址 ...

  9. [转载]linux下编译php中configure参数具体含义

    编译N次了   原来这么回事 原文地址:linux下编译php中configure参数具体含义作者:捷心特 php编译参数的含义 ./configure –prefix=/usr/local/php ...

随机推荐

  1. 《Photoshop 2020》初心版_v6 21.0.2.57

    <Phtoshop 2020>初心版_v6 下载地址(5245) SHA1:E926A1B99D147A27A44050A5BCE2E69E2CDAEEAE 版本信息    发行版本 20 ...

  2. java学习笔记之IO编程—目录和文件的拷贝

    进行文件或目录的拷贝时,要先判断处理对象是文件还是目录,如果是文件则直接拷贝,如果是目录还需要拷贝它的子目录及其文件,这就需要递归处理了 import java.io.*; class FileUti ...

  3. Dubbo-服务注册中心之AbstractRegistry

    在dubbo中,关于注册中心Registry的有关实现封装在了dubbo-registry模块中.提供者(Provider)个消费者(Consumer)都是通过注册中心进行资源的调度.当服务启动时,p ...

  4. Dubbo的SPI机制与JDK机制的不同及原理分析

    从今天开始,将会逐步介绍关于DUbbo的有关知识.首先先简单介绍一下DUbbo的整体概述. 概述 Dubbo是SOA(面向服务架构)服务治理方案的核心框架.用于分布式调用,其重点在于分布式的治理. 简 ...

  5. Codeforces Round #350 (Div. 2)(670C)

    今天对着算法进阶指南,学了一下离散化.大概对桶排这样的算法优化比较好吧. 离散化:就是把无穷大的集合中若干个元素映射为有限集合以便于统计的方法.例如在很多时候,问题范围定义为整数集合Z,但涉及的元素只 ...

  6. 缓存 - 数据缓存 - IndexedDB - Dexie.js

    Classes Dexie DexieError Collection and():Add JS based criteria to collection(向集合添加基于JS的条件) delete() ...

  7. 安装SQL Server2008出现Restart computer failed的解决办法

    1.打开注册表编辑器 2.找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager双击文件夹 3.找到PendingF ...

  8. Error Code : 1064 You have an error in your SQL syntax; check the manual that corresponds to your My

    转自:https://blog.csdn.net/haha_66666/article/details/78444457 Query : select * from order LIMIT 0, 10 ...

  9. 获取mybaties插入记录自动增长的主键值

    首先在Mybatis Mapper文件中insert语句中添加属性“useGeneratedKeys”和“keyProperty”,其中keyProperty是保存主键值的属性. 例如: <in ...

  10. C# asp.net 配置文件连接sql 数据库

    先引用 using System.Configuration;//配置文件using System.Data.SqlClient; 我这里使用的是SqlServer 2008  sa 用户 密码也为s ...