1.从.c文件到可执行文件,其间经历了几步?

高级语言是偏向人,按照人的思维方式设计的,机器对这些可是莫名奇妙,不知所谓。那从高级语言是如何过渡到机器语言的呢?这可是一个漫长的旅途呀!

其中,得经历这样的历程:C源程序->编译预处理->编译->汇编程序->链接程序->可执行文件

1.预处理  读取c源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理。伪指令主要包括以下四个方面:

(1)宏定义指令,如#define Name TokenString,#undef等。对于前一个伪指令,预编译所要作得的是将程序中的所有Name用TokenString替换,但作为字符串常量的Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。

(2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif,等等。这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。

(3)加载头文件,如#include"FileName"或者#include<FileName>等。采用头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条#include语句即可,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在/usr/include目录下。在程序中#include它们要使用尖括号(<>)。另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在#include中要用双引号("")。

预编译是将.c 文件转化成 .i文件,  

重定向使用的gcc命令是:gcc –E hello.c >hello.i

在预处理阶段是不做语法检查的。

2.编译阶段 :

需要进行三个步骤:词法分析、语法分析和语义分析

在linux环境中,输入命令:gcc–s  hello.c  参数c告诉gcc命令只进行编译,不做其他处理。命令运行结束后产生hello.o的目标文件。

3.汇编过程

编译过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。

输入命令:gcc –c hello.c

就会生成hello.o的目标文件。

4.链接过程

链接就是将不同部分的代码和数据收集和组合成为一个单一文件的过程,这个文件可被加载或拷贝到存储器执行.
  链接可以执行与编译时(源代码被翻译成机器代码时),也可以执行与加载时(在程序被   加载器加载到存储器并执行时),甚至执行与运行时,由应用程序来执行.在现代系统中,    链接是由链接器自动执行的.
  链接器分为:静态链接器和动态链接器两种.
(1).静态链接器
  静态链接器以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件作为输出.

  静态链接器主要完成两个任务:
  1>符号解析:目标文件定义和引用符号.符号解析的目的在于将每个符号引用和一个符号定义联系起来.
  2>重定位:编译器和汇编器生成从地址零开始的代码和数据节.链接器通过把每个符号定义和一个存储器位置联系起来,然后修改所有对这些符号的引用,使得他们执行这个存储位置,从而重定位这些节.

(2)动态链接器
  共享库是一个目标模块,在运行时,可以加载到任意的存储器地址,并在存储器中和一个程序链接起来.这个过程称为动态链接,是由动态链接器完成的.
  共享库的共享在两个方面有所不同.首先,在任何给定的文件系统中,对于一个库只有一个.so文件.所有引用该库德可执行目标文件共享这个.so文件中的代码和数据,而不是像静态库的内容那样被拷贝和嵌入到引用它们的可执行的文件中.其次,在存储器中,一个共享库的.text只有一个副本可以被不同的正在运行的进程共享。

2.-O1,-O2,-O3为何方神圣,它们是如何优化编译文件的?

(1)首先,她们的真面目是:

-O1 提供基础级别的优化

-O2提供更加高级的代码优化,会占用更长的编译时间

-O3提供最高级的代码优化

可以使用-f命令行选项引用每个单独的优化技术。

1, 编译器优化级别1

在优化的第一个级别执行基础代码的优化

这个级别试图执行9种单独的优化功能:

(1).-fdefer-pop: 这种优化技术与汇编语言代码在函数完成时如何进行操作有关。

(2).-fmerge-constans: 使用这种优化技术, 编译器试图合并相同的常量.

(3) . -fthread-jumps: 使用这种优化技术与编译器如何处理汇编代码中的条件和非条件分支有关。 在某些情况下, 一条跳转指令可能转移到另一条分支语句。 通过一连串跳转, 编译器确定多个跳转之间的最终目标并且把第一个跳转重新定向到最终目标。

(4).-floop-optimize:通过优化如何生成汇编语言中的循环, 编译器可以在很大程序上提高应用程序的性能。通常, 程序由很多大型且复杂的循环构成。 通过删除在循环内没有改变值的变量赋值操作, 可以减少循环内执行指令的数量, 在很大程度上提高性能。 此外优化那些确定何时离开循环的条件分支,以便减少分支的影响。

(5).-fif-conversion: if-then语句应该是应用程序中仅次于循环的最消耗时间的部分。简单的if-then语句可能在最终的汇编语言代码中产生众多的条件分支。 通过减少或者删除条件分支, 以及使用条件传送 设置标志和使用运算技巧来替换他们, 编译器可以减少if-then语句中花费的时间量。

(6)-fif-conversion2: 这种技术结合更加高级的数学特性, 减少实现if-then语句所需的条件分支。

(7)-fdelayed-branch: 这种技术试图根据指令周期时间重新安排指令。 它还试图把尽可能多的指令移动到条件分支前, 以便最充分的利用处理器的治理缓存。

(8) -fguess-branch-probability:就像其名称所暗示的, 这种技术试图确定条件分支最可能的结果, 并且相应的移动指令, 这和延迟分支技术类似。因为在编译时预测代码的安排,所以使用这一选项两次编译相同的c或者c++代码很可能会产生不同的汇编语言代码,这取决于编译时编译器认为会使用那些分支。

(9)-fcprop-registers: 因为在函数中把寄存器分配给变量, 所以编译器执行第二次检查以便减少调度依赖性(两个段要求使用相同的寄存器)并且删除不必要的寄存器复制操作。

2, 编译器优化级别2

结合了第一个级别的所有优化技术,再加上一下一些优化:

(1)-fforce-mem: 这种优化在任何指令使用变量前, 强制把存放再内存位置中的所有变量都复制到寄存器中。 对于只涉及单一指令的变量, 这样也许不会有很大的优化效果. 但是对于在很多指令(必须数学操作)中都涉及到的变量来说, 这会时很显著的优化, 因为和访问内存中的值相比 ,处理器访问寄存器中的值要快的多。

(2)-foptimize-sibling-calls: 这种技术处理相关的和/或者递归的函数调用。通常,递归的函数调用可以被展开为一系列一般的指令, 而不是使用分支。

(3)-fstrength-reduce: 这种优化技术对循环执行优化并且删除迭代变量。 迭代变量是捆绑到循环计数器的变量, 比如使用变量, 然后使用循环计数器变量执行数学操作的for-next循环。

(4)-fgcse: 这些优化操作试图分析生成的汇编语言代码并且结合通用片段, 消除冗余的代码段。如果代码使用计算性的goto,gcc指令推荐

(5)-fcse-follow-jumps: 这种特别的通用子表达式消除技术扫描跳转指令, 查找程序中通过任何其他途径都不会到达的目标代码。这种情况最常见的例子就式if-then-else语句的else部分。

(6)-frerun-cse-after-loop: 这种技术在对任何循环已经进行过优化之后重新运行通用子表达式消除例程。这样确保在展开循环代码之后更进一步地优化还编代码。

(7)-fdelete-null-pointer-checks: 这种优化技术扫描生成的汇编语言代码, 查找检查空指针的代码。

(8)-fextensive-optimizations: 这种技术执行从编译时的角度来说代价高昂的各种优化技术,但是它可能对运行时的性能产生负面影响。

(9)-fregmove: 编译器试图重新分配mov指令中使用的寄存器, 并且将其作为其他指令操作数, 以便最大化捆绑的寄存器的数量。

(10)-fschedule-insns: 编译器将试图重新安排指令, 以便消除等待数据的处理器。对于在进行浮点运算时有延迟的处理器来说, 这使处理器在等待浮点结果时可以加载其他指令。

(11)-fsched-interblock: 这种技术使编译器能够跨越指令块调度指令。 这可以非常灵活地移动指令以便等待期间完成的工作最大化。

(12)-fcaller-saves: 这个选项指示编译器对函数调用保存和恢复寄存器, 使函数能够访问寄存器值, 而且不必保存和恢复他们。 如果调用多个函数, 这样能够节省时间, 因为只进行一次寄存器的保存和恢复操作, 而不是在每个函数调用中都进行。

(13)-fpeephole2: 这个选项允许进行任何计算机特定的观察孔优化。

(14)-freorder-blocks: 这种优化技术允许重新安排指令块以便改进分支操作和代码局部性。

(15)-fstrict-aliasing: 这种技术强制实行高级语言的严格变量规则。 对于c和c++程序来说, 它确保不在数据类型之间共享变量. 例如, 整数变量不和单精度浮点变量使用相同的内存位置。

(16)-funit-at-a-time:这种优化技术指示编译器在运行优化例程之前读取整个汇编语言代码。这使编译器可以重新安排不消耗大量时间的代码以便优化指令缓存。

(17)-falign-functions:这个选项用于使函数对准内存中特定边界的开始位置。大多数处理器按照页面读取内存,并且确保全部函数代码位于单一内存页面内, 就不需要叫化代码所需的页面。

(18)-fcrossjumping: 这是对跨越跳转的转换代码处理, 以便组合分散在程序各处的相同代码。 这样可以减少代码的长度,但是也许不会对程序性能有直接影响。

3, 编译器优化级别3

它整合了第一和第二级别中的左右优化技巧, 还包括一下优化:

-finline-functions:这种优化技术不为函数创建单独的汇编语言代码,而是把函数代码包含在调度程序的

代码中。 对于多次被调用的函数来说, 为每次函数调用复制函数代码。 虽然这样对于减少代码长度不利, 但是通过最充分的利用指令缓存代码, 而不是在每次函数调用时进行分支操作, 可以提高性能。

-fweb: 构建用于保存变量的伪寄存器网络。 伪寄存器包含数据, 就像他们是寄存器一样, 但是可以使用各种其他优化技术进行优化, 比如cse和loop优化技术。

-fgcse-after-reload:这中技术在完全重新加载生成的且优化后的汇编语言代码之后执行第二次gcse优化,帮助消除不同优化方式创建的任何冗余段。

[转帖]-O1,-O2,-O3编译优化知多少的更多相关文章

  1. gcc -O0 -O1 -O2 -O3 四级优化选项及每级分别做什么优化【转】

    转自:http://blog.csdn.net/qinrenzhi/article/details/78334677 相关博客http://blog.chinaunix.net/uid-2495495 ...

  2. gcc -O0 -O1 -O2 -O3 四级优化选项及每级分别做什么优化

    参考链接 : http://blog.csdn.net/qq_31108501/article/details/51842166 gcc -D选项的作用,声明宏 参考链接:  http://blog. ...

  3. gcc 优化选项 -O1 -O2 -O3 -Os 优先级

    http://hi.baidu.com/xiaole10368/item/7cea9b1369cc240db88a1a5c 少优化->多优化: O0 -->> O1 -->&g ...

  4. gcc 优化选项 -O1 -O2 -O3 -Os 优先级,-fomit-frame-pointer

    英文:https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Optimize-Options.html#Optimize-Options 少优化->多优化: ...

  5. Object.assign(o1, o2, o3) 对象 复制 合拼

    Object 对象方法学习之(1)—— 使用 Object.assign 复制对象.合并对象 合并对象 var o1 = {a: 1}; var o2 = {b: 2}; var o3 = {c: 3 ...

  6. -O1 -O2 -O3 优化的原理是什么?

    一般来说,如果不指定优化标识的话,gcc就会产生可调试代码,每条指令之间将是独立的:可以在指令之间设置断点,使用gdb中的 p命令查看变量的值,改变变量的值等.并且把获取最快的编译速度作为它的目标. ...

  7. GCC 优化选项 -O1 -O2 -O3 -OS 优先级,-FOMIT-FRAME-POINTER(O3的优化很小,只增加了几条优化而已)

    四种编译优化类型的解释: `-O ' `-O1 '                 Optimize.      Optimizing   compilation   takes   somewhat ...

  8. 【linux基础】关于ARM板子使用O3编译选项优化

    前言 应领导要求需要将最初级版本的算法移植到ARM板子上,并进行优化,以期达到实时. 平台 移植前: TX2 移植后: ARM() processor : model name : ARMv7 Pro ...

  9. 痞子衡嵌入式:MCUXpresso IDE下设置代码编译优化等级的几种方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是MCUXpresso IDE下设置代码编译优化等级的几种方法. 最近公司芯片设计团队正在开发一款全新的基于 Cortex-M33 内核的 ...

  10. GCC 编译优化指南(转)

    GCC 编译优化指南(转) http://www.jinbuguo.com/linux/optimize_guide.html 作者:金步国 版权声明 本文作者是一位开源理念的坚定支持者,所以本文虽然 ...

随机推荐

  1. 聊聊游戏业务怎么用高斯Redis

    摘要:其实游戏客户对数据库的诉求是很明确的,数据库应当"放心存放心用". 本文分享自华为云社区<华为云GaussDB(for Redis)揭秘第27期:聊聊游戏业务怎么用高斯 ...

  2. 4种方法帮你解决IntelliJ IDEA控制台中文乱码问题

    摘要:在本文中总结了4 种方法完美解决 IntelliJ IDEA 控制台中文乱码问题. 前言 IntelliJ IDEA 如果不进行配置的话,运行程序时控制台中文乱码问题会非常严重,严重影响我们对信 ...

  3. 13个QA带你了解线上压测的知识点

    摘要:设计一个线上压测系统能让我们学习到多少东西?这13个问题看你能否搞定. 本文分享自华为云社区<设计一个线上压测系统能让我们学习到多少东西?13个问题看你能否搞定>,作者:breakD ...

  4. 论文解读丨LayoutLM: 面向文档理解的文本与版面预训练

    摘要:LayoutLM模型利用大规模无标注文档数据集进行文本与版面的联合预训练,在多个下游的文档理解任务上取得了领先的结果. 本文分享自华为云社区<论文解读系列二十五:LayoutLM: 面向文 ...

  5. 无法获得数据库 'model' 上的排他锁。请稍后重试该操作

    标题: Microsoft SQL Server Management Studio 数据库 "XXXX" 的 创建 失败. (Microsoft.SqlServer.Smo) 有 ...

  6. Kubernetes(K8S) 监控 Prometheus + Grafana

    监控指标 集群监控 节点资源利用率 节点数 运行Pods Pod 监控 容器指标 应用程序 Prometheus 开源的 监控.报警.数据库 以HTTP协议周期性抓取被监控组件状态 不需要复杂的集成过 ...

  7. Kubernetes(K8S) helm 安装

    Helm 是一个 Kubernetes 的包管理工具, 就像 Linux 下的包管理器, 如 yum/apt 等, 可以很方便的将之前打包好的 yaml 文件部署到 kubernetes 上. Hel ...

  8. MySQL 创建存储过程注意项

    MySQL server version for the  right syntax to use near 'IF' MySQL server version for the right synta ...

  9. 【设计模式】分享 Java 开发中常用到的设计模式(一)

    分享 Java 开发中常用到的设计模式(一) 前言 不知道大家在开发的时候,有没有想过(遇到)这些问题: 大家都是按需要开发,都是一个职级的同事,为什么有些人的思路就很清晰,代码也很整洁.易懂:而自己 ...

  10. 什么是「滑动窗口算法」(sliding window algorithm),有哪些应用场景?

    今天是算法数据结构专题的第2篇文章,我们一起来学习一下「滑动窗口算法」. 前言 最近刷到leetCode里面的一道算法题,里面有涉及到Sliding windowing算法,因此写一篇文章稍微总结一下 ...