当前位置: 首页 > 编程日记 > 正文

C C++的编译过程详解

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++编译过程
对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动
态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一
些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一

定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。

-----------------------------------------------------------------------------------------------------------------------------------作者  张彦升

转载于:https://www.cnblogs.com/ainima/p/6331152.html

相关文章:

python queue 调试_学Python不是盲目的,是有做过功课认真去了解的

有多少伙伴是因为一句‘人生苦短&#xff0c;我用Python’萌生想法学Python的&#xff01;我跟大家更新过很多Python学习教程普及过多次的Python相关知识&#xff0c;不过大家还是还得计划一下Python学习路线&#xff01;Python入门前&#xff0c;你必须得知道这些&#xff0c;…

嘿嘿 刚刚进来 记录下

大家好&#xff0c;小弟刚刚进来&#xff0c;记录一下&#xff0c;发现这个网站真是太好了&#xff01;&#xff01;转载于:https://blog.51cto.com/wikowin/1112039

Oracle数据库查看表空间是否为自增的

表空间是有数据文件组成的&#xff0c;所以看表空间是否自增即看数据文件&#xff0c;如下查自增的表空间&#xff1a; select tablespace_name,file_name,autoextensible from dba_data_files where autoextensibleYES; autoextensible: YES/NO 即表示是否自增。 转载于:https…

C++多线程:异步操作std::async和std::promise

文章目录std::async简介使用案例std::promise简介成员函数总结之前的文章中提到了C多线程中的异步操作机制 C 多线程&#xff1a;future 异步访问类&#xff08;线程之间安全便捷的数据共享)&#xff0c;接下来分享关于异步操作中 async和 promise的相关使用总结。std::async …

5如何将表格的一行数据清空_微信公众号推文中如何自定义添加表格?

微信公众号发的图文消息里经常需要有表格&#xff0c;因为表格进行数据展现更直观明了&#xff0c;所以接下来就给大家分享一下如何自定义添加表格。打开小蚂蚁编辑器&#xff0c;在编辑区点击鼠标右键&#xff0c;在出现的选项中选择【插入表格】。系统会默认添加一个5行5列的…

基于visual Studio2013解决面试题之0901奇偶站队

&#xfeff;&#xfeff;&#xfeff;题目解决代码及点评/*给定一个存放整数的数组&#xff0c;重新排列数组使得数组左边为奇数&#xff0c;右边为偶数 解决方法&#xff1a;两边同时遍历&#xff0c;如果遇到左边偶数或者右边基数&#xff0c;则交换 */#include <iostrea…

联想架构调整:智能手机业务很重要

1月5日&#xff0c;杨元庆宣布联想集团进行组织架构调整。将联想旗下业务拆分为两个新的端到端业务集团—— Lenovo 业务集团和 Think 业务集团。杨元庆在内部邮件如是说:“Lenovo业务集团(Lenovo Business Group&#xff0c;简称LBG)&#xff0c;由刘军领军&#xff0c;致力于…

activiti5/6 系列之--BpmnModel使用

BpmnModel对象&#xff0c;是activiti动态部署中很重要的一个对象&#xff0c;如果BpmnModel对象不能深入的理解&#xff0c;那可能如果自己需要开发一套流程设计器&#xff0c;使用bpmn-js使用前端或者C/S展现流程流转而不是使用工作流引擎&#xff0c;就显得力不从心。例如&a…

C++多线程:package_task异步调用任何目标执行操作

文章目录描述函数成员及使用总结我们上一篇描述关于C多线程中的异步操作相关库( async和 promise)&#xff0c;本节将分享c标准库中最后一个多线程异步操作库 package_task的学习笔记。描述 头文件 <future> 声明方式: template< class R, class ...Args > class …

ICE BOX 配置,使用----第一篇

一 理论部分 (1) 为什么要使用icebox? icebox server代替了通常的server. icebox是为了方便集中管理多个ice服务而建立的。 它通过使用icebox服务器&#xff0c;把ice服务注册进去&#xff0c;从而建立联系。 所以它除了建立传统的ice服务器&#xff0c;ice客户端&#xff0c;…

测试打桩_DNF:CEO实测旭旭宝宝红眼,打桩高达2494E,伤害超越狂人剑魂

对于红眼这个职业&#xff0c;旭旭宝宝倾注了太多的心血&#xff0c;耗资几千万打造而成。虽然&#xff0c;作为固伤职业&#xff0c;在伤害方面&#xff0c;不及剑魂和剑帝这类百分比&#xff0c;但因人数颇多&#xff0c;从而被广泛关注。而今&#xff0c;旭旭宝宝的红眼&…

122112_1452_Word1

122112_1452_Word1转载于:https://www.cnblogs.com/joshuali/archive/2013/01/11/4339303.html

C++ 多线程:时间控制

C多线程库中的各个子库都有各自的时间控制方式&#xff0c;依此来进行多线程程序运行中cpu资源的精确控制。 使用std::chrono时间库可以提供微妙、毫秒、秒及以上的时间取用,并且能够获取当前系统时间。 如下代码 #include <iostream> #include <fstream> #inclu…

array用法 numpy_NumPy总结(基础用法)

numpy可以说是Python运用于人工智能和科学计算的一个重要基础&#xff0c;近段时间恰好学习了numpy&#xff0c;pandas&#xff0c;sklearn等一些Python机器学习和科学计算库&#xff0c;因此在此总结一下常用的用法。引入numpy库的方式&#xff1a;import numpy as np1、numpy…

python 字符串 转 dict

比直接eval更好的方法>>>import ast >>>ast.literal_eval("{muffin : lolz, foo : kitty}") {muffin:lolz,foo:kitty} 用 json 遇到问题&#xff1a; >>> import json json.loads({"x": 1,"y":2}) {uy: 2, ux: 1…

rhino-java中调用javascript

2019独角兽企业重金招聘Python工程师标准>>> 在有些情况下需要在java里面执行javascript&#xff0c;这时rhino就可以帮忙了。mozilla的一个开源产品。 官网https://developer.mozilla.org/en-US/docs/Rhino 之前的一篇博客http://my.oschina.net/yybear/blog/101…

HDU 2566 统计硬币

统计硬币 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 9633 Accepted Submission(s): 6505 Problem Description 假设一堆由1分、2分、5分组成的n个硬币总面值为m分&#xff0c;求一共有多少种可能的组合方式&…

linux C 多线程编程

文章目录多线程的一些小知识&#xff1a;1创建线程 pthread_create2线程挂起 pthread_join3线程终止 pthread_exit4线程分离 pthread_detach5线程取消 pthread_cancel线程同步 pthread_mutex_t互斥变量我们在写linux的服务的时候&#xff0c;经常会用到linux的多线程技术以提高…

JavaSE replaceAll 方法

private String srcStr "index\\.php\\?action";//要替换的原字符串 private String destStr "index.php?<{\\$LANGUAGE_TYPE}>action";//目的字符串 注意.和?都要转义&#xff0c;因此需要在前面添加两个反斜杠。 关于第二个参数&#xff0c;$…

elementui datetimepicker 移动端_在 Gitee 收获 2.5K Star,前后端分离的 RuoYi 它来了

作为 2019 年 Gitee 上最受欢迎的开源项目&#xff0c;权限管理系统 RuoYi 已经在 Gitee 上获得了超过 11K 的 Star。 这次作者若依推出了基于 SpringBoot Vue Element UI 的前后端分离版本的 RuoYi-Vue&#xff0c;方便有前后端分离开发需求的同学使用。项目名称&#xff1a…

用MyEclipse开发Spring入门

1 新建一个项目 File----->New ----->Project 在出现的对话框中选择 MyEclipse 下的 Web Project&#xff0c;在Project Name 输入mySpring1&#xff0c;其他的选项默认&#xff0c;再点Finish完成&#xff1b; 2 加入Spring 包 在myspring1 项目上点右键&#xff0c;选…

带你轻而易举的学习python——八皇后问题

首先我们来看一下这个著名的八皇后问题 八皇后问题&#xff1a;在88格的国际象棋上摆放八个皇后&#xff0c;使其不能互相攻击&#xff0c;即任意两个皇后都不能处于同一行、同一列或同一斜线上&#xff0c;问有多少种摆法。 在这个问题提出之后人们又将它扩展到了nn格的棋盘摆…

ubuntu18.04.1内核升级至5.0.0-25版本

ubuntu18.04操作系统版本先已支持在线的内核版本升级&#xff0c;到目前为止18.04发布版已经拥有三个小版本了1,2,3。 其中18.04.01和18.04.03版本&#xff0c;安装好之后默认的是4.15内核版本&#xff0c;但是默认支持在线安装4.18和5.0.0内核版本。 具体升级步骤如下&#x…

输出n行杨辉三角数

1 /*2 输出n行杨辉三角数 3 输入n&#xff0c;n是1&#xff5e;100之间的整数 4 */5 #include<stdio.h>6 int main()7 {8 int a[100],b[100];9 int i,j; 10 int n; 11 scanf("%d",&n); 12 if(n1) 13 { 14 printf("1\…

怎么扫描_打印机上扫描仪怎么用 打印机上扫描仪使用及添加方法

打印机是生活中常用的打印设备&#xff0c;主要用于连接 电脑 打印电脑上的文件&#xff0c;方便办公。对于第一次使用打印机的朋友可能还不是很熟悉如何使用&#xff0c;比如打印机上 扫描仪 怎么用&#xff1f;怎么添加打印机扫描仪&#xff1f;下面小编就来为大家介绍下吧。…

java 调用webservice的各种方法总结

http://www.blogjava.net/zjhiphop/archive/2009/04/29/webservice.html 现在webservice加xml技术已经逐渐成熟&#xff0c;但要真正要用起来还需时日!! 由于毕业设计缘故&#xff0c;我看了很多关于webservice方面的知识&#xff0c;今天和大家一起来研究研究webservice的…

vc++图像保存,重绘

新建mfc应用程序&#xff0c;单文档 增加绘图 分别增加命令响应 添加成员变量UINIT 图形可以运行&#xff0c;如何保存呢&#xff1f;&#xff08;一个集合类&#xff0c;CPtArt&#xff09; 用一个类的对象来保存一个图形的三个要素 所以插入一个新的类&#xff08;通常的类&a…

linux 进程内存分布及 堆分配和栈分配的特点

文章目录进程内存空间分布size命令查看内存分布堆方式内存分配和栈方式内存分配比较使用stap 深入追踪malloc逻辑进程内存空间分布 一个程序的内存空间主要如下&#xff1a; 代码段(text segment)&#xff1a;只读权限&#xff1b;常是指用来存放程序执行代码的一块内存区域&…

echarts 坐标自适应_echarts 同一页面,多个图表 页面大小自适应

// 路径配置require.config({paths: {echarts: ./js}});// 使用require([echarts,echarts/chart/line, // 折线图echarts/chart/bar // 柱状图],function (ec) {var myChart ec.init(document.getElementByIdx_x(main));var myChartx ec.init(document.getElementByIdx_x(main…

opencv——pcb上寻找mark点(拟合椭圆的方法)

#include "stdafx.h" // FitCircle.cpp : 定义控制台应用程序的入口#include "cv.h" #include "highgui.h" #include "cxcore.h" #include "cvaux.h" #include <iostream>using namespace cv; using namespace std; v…