【转载】C/C++编译过程分析
转自:http://www.360doc.com/content/14/0109/16/835125_343879650.shtml
- C/C++编译过程
- C/C++编译过程主要分为4个过程
- 1) 编译预处理
- 2) 编译、优化阶段
- 3) 汇编过程
- 4) 链接程序
- 一、编译预处理
- (1)宏定义指令,如#define Name TokenString,#undef等。 对于前一个伪指令,预编译所要做的是将程序中的所有Name用TokenString替换,
- 但作为字符串常量的 Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。
- (2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif等。 这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。
- 预编译程序将根据有关的文件,将那些不必要的代码过滤掉
- (3) 头文件包含指令,如#include "FileName"或者#include <FileName>等。 在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),
- 同时包含有各种外部符号的声明。 包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在/usr/include目录下。
- 在程序中#include它们要使用尖括号(< >)。
- 另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在#include中要用双引号("")。
- (4)特殊符号,预编译程序可以识别一些特殊的符号。 例如在源程序中出现的#line标识将被解释为当前行号(十进制数),
- 上面程序实现了对宏line的运用
- (5)预处理模块 预处理工作由#pragma命令完成,#Pragma命令将设定编译器的状态或者是指示编译器完成一些特定的动作。
- #pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。
- 依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
- 打开C标准库函数,如stdio.h,我们总能找到下面这一句指示编译器初始化堆栈
- #include "iostream"
- #line 100
- using namespace std;
- int main(int argc, char* argv[])
- {
- cout<<"__LINE__:"<<__LINE__<<endl;
- return 0;
- }
- /*--------------------
- * 输出结果为:
- * __LINE__:103
- * 本来输出的结果应该是 7,但是用#line指定行号之后,使下一行的行号变为,
- * 到输出语句恰为行103
- ---------------------*/
- C/C++编译过程
- 或者程序指示编译器去链接系统动态链接库或用户自定义链接库
- 二、编译、优化阶段
- 经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main,if,else,for,while,{,}, +,-,*,\等等。
- 在《编译原理》中我们可以了解到一个编译器对程序代码的编译主要分为下面几个过程:
- a) 词法分析
- b) 语法分析
- c) 语义分析
- d) 中间代码生成
- e) 代码优化
- f) 代码生成
- g) 符号表管理
- h) 将多个步骤组合成趟
- i) 编译器构造工具
- 在这里我们主要强调对函数压栈方式(函数调用约定)的编译处理
- C与C++语言调用方式大体相同,下面是几种常用的调用方式:
- __cdecl 是C DECLaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,
- 这些参数由调用者清除,称为手动清栈。被调用函数不需要求调用者传递多少参数,调用者传递过多或者过少的参数,
- 甚至完全不同的参数都不会产生编译阶段的错误。
- _stdcall 是StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,
- 最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除,使用的指令是 retnX,X表示参数占用的字节数,
- CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。函数在编译的时候就必须确定参数个数,
- 并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。
- PASCAL 是Pascal语言的函数调用方式,在早期的c/c++语言中使用这种调用方式,
- 参数压栈顺序与前两者相反,但现在我们在程序中见到的都是它的演化版本,其实
- #pragma comment(lib,_T("GDI32.lib"))
- #ifdef _MSC_VER
- /*
- * Currently, all MS C compilers for Win32 platforms default to 8 byte
- * alignment.
- */
- #pragma pack(push,_CRT_PACKING)
- #endif /* _MSC_VER */
- C/C++编译过程
- 质是另一种调用方式
- _fastcall是编译器指定的快速调用方式。由于大多数的函数参数个数很少,使用堆栈传递比较费时。因此_fastcall通常规定将前两个(或若干个)参数由寄存器传递,其余参数还是通过堆栈传递。不同编译器编译的程序规定的寄存器不同。返回方式和_stdcall相当。
- _thiscall 是为了解决类成员调用中this指针传递而规定的。_thiscall要求把this指针放在特定寄存器中,该寄存器由编译器决定。VC使用ecx,Borland的C++编译器使用eax。返回方式和_stdcall相当。
- _fastcall 和 _thiscall涉及的寄存器由编译器决定,因此不能用作跨编译器的接口。所以Windows上的COM对象接口都定义为_stdcall调用方式。
- C中不加说明默认函数为_cdecl方式(C中也只能用这种方式),C++也一样,但是默认的调用方式可以在IDE环境中设置。简单的我们可以从printf函数看出
- printf使用从从左至右压栈,返回int型并由_CRTIMP指定封在动态链接库中。
- 通过金典的hello world程序我们可以知道编译器对其argc和argv[]这两个参数进行了压栈,并且argc留在了栈顶
- 优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化处理主要分为下面几个过程:
- 1) 局部优化
- a) 基本块的划分
- b) 基本块的变换
- c) 基本块的DAG表示
- d) DAG的应用
- e) 构造算法讨论
- 2) 控制流分析和循环优化
- a) 程序流图与循环
- /*金典的hello world*/
- #include <stdio.h>
- int main(int argc, char* argv[])
- {
- printf("hello world");
- return 0;
- }
- _Check_return_opt_ _CRTIMP int __cdecl printf(_In_z_ _Printf_format_string_ const char * _Format, ...);
- #define CALLBACK _stdcall /* Windows程序回调函数*/
- #define WINAPI _stdcall
- #define WINAPIV _cdecl
- #define PASCAL _stdcall /*在c++语言中使用了StandardCall调用方式*/
- #define PASCAL _cdecl/*在c语言中使用了C DECLaration调用方式*/
- C/C++编译过程
- b) 循环
- c) 循环的查找
- d) 可归约流图
- e) 循环优化
- 3) 数据流的分析与全局优化
- a) 一些主要的概念
- b) 数据流方程的一般形式
- c) 到达一定值数据流方程
- d) 可用表达式及其数据流方程
- e) 活跃变量数据流方程
- f) 复写传播
- 经过优化得到的汇编代码必须经过汇编程序的汇编转换成相应的机器指令,方可能被机器执行。
- 三、汇编过程
- 汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,
- 都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。
- 目标文件由段组成。通常一个目标文件中至少有两个段: 代码段:该段中所包含的主要是程序的指令。
- 该段一般是可读和可执行的,但一般却不可写。 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。
- 四、链接程序
- 由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。
- 例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);
- 在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
- 链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,
- 使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。
- 根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:
- (1)静态链接 在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。
- 这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,
- 其中的每个文件含有库中的一个或者一组相关函数的代码。
- (2) 动态链接
- 在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。
- 链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量
- 的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应
- 进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。
- C/C++编译过程
- 对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动
- 态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一
- 些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一
- 定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。
【转载】C/C++编译过程分析的更多相关文章
- u-boot-2016.09 make编译过程分析(二)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/guyongqiangx/article/ ...
- u-boot-2016.09 make编译过程分析(一)
https://blog.csdn.net/guyongqiangx/article/details/52565493 综述 u-boot自v2014.10版本开始引入KBuild系统,Makefil ...
- <转载>gcc/g++编译
转载于:http://www.cnblogs.com/yc_sunniwell/archive/2010/07/22/1782678.html 1. gcc/g++在执行编译工作的时候,总共需要4步 ...
- Linux移植之make uImage编译过程分析
编译出uboot可以运行的linux内核代码的命令是make uImage,下面详细介绍下生成linux-2.6.22.6/arch/arm/boot/uImage的过程: 1.vmlinux.Ima ...
- [转载]Linux内核编译
原文地址:https://blog.csdn.net/qq_34247099/article/details/50949720 写在前面的话: 本人大二,东南大学一个软工狗,正在修一门名为<操作 ...
- (转载)JAVA动态编译--字节代码的操纵
在一般的Java应用开发过程中,开发人员使用Java的方式比较简单.打开惯用的IDE,编写Java源代码,再利用IDE提供的功能直接运行Java 程序就可以了.这种开发模式背后的过程是:开发人员编写的 ...
- 转载:回编译APK出错:java.nio.char set.MalformedInputException: Input length = 1
使用APKtool回编译APK,出现错误如下: Exception in thread "main" org.yaml.snakeyaml.error.YAMLExcepti ...
- (转载)反编译android的apk文件步骤
下面的方法我已经尝试过,完全可以成功,重点的步骤我在这里说一下 1.必须要有java环境,记得配置好环境变量 2.如果只查看class中的函数文件,只需要下载dex2jar和jd-gui 3.下载地址 ...
- [转载]linux下编译php中configure参数具体含义
编译N次了 原来这么回事 原文地址:linux下编译php中configure参数具体含义作者:捷心特 php编译参数的含义 ./configure –prefix=/usr/local/php ...
随机推荐
- ASP.NET MVC中ActionResult的不同返回方式
1.返回视图 return View();//返回方法名对应的视图 return View("aaa");//返回名称为aaa的视图 2.返回文本内容 return Content ...
- MySQL 8 通用二进制发行版安装
安装前的一些说明: 检查平台兼容性: https://www.mysql.com/support/supportedplatforms/database.html 如果是在RedHat7版本安装的话, ...
- vue自学入门-5(vuex state)
vue自学入门-1(Windows下搭建vue环境) vue自学入门-2(vue创建项目) vue自学入门-3(vue第一个例子) vue自学入门-4(vue slot) vue自学入门-5(vuex ...
- BZOJ 3143 游走
Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点, ...
- BZOJ 2467: [中山市选2010]生成树
有一种图形叫做五角形圈.一个五角形圈的中心有1个由n个顶点和n条边组成的圈.在中心的这个n边圈的每一条边同时也是某一个五角形的一条边,一共有n个不同的五角形.这些五角形只在五角形圈的中心的圈上有公共的 ...
- cookie、session和application
https://cloud.tencent.com/developer/article/1493869 前言: 一直想写一篇关于cookie和session的博客,由于种种原因,一直没有整理,这不,今 ...
- 关于Euler-Poisson积分的几种解法
来源:https://www.cnblogs.com/Renascence-5/p/5432211.html 方法1:因为积分值只与被积函数和积分域有关,与积分变量无关,所以\[I^{2}=\left ...
- Babel 7 主要改变
1.不支持Node:0.10,0.12,4,5版本 2.更换命名-@babel/xxx 3.移除以年份命名的presets,统一更换成@babel/preset-env 4.移除 ’Stage‘ pr ...
- Linux运维--14.Kolla部署OpenStack使用external MariaDB Galera Cluster
使用haproxy+keepalived实现Mariadb负载均衡 controller2: 10.100.2.52 haproxy+keepalived controller3: 10.100.2. ...
- Python 类方法、实例方法、静态方法的使用与及实例
类方法 使用装饰器@classmethod 第一个参数必须是当前类对象,该参数名一般约定为“cls” (可修改但不建议)通过他来传递类的属性和方法(不能传实例的属性和方法) 调用:实例对象和类对象多可 ...