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. C++篇:第十二章_文件及IO_知识点大全

    C++篇为本人学C++时所做笔记(特别是疑难杂点),全是硬货,虽然看着枯燥但会让你收益颇丰,可用作学习C++的一大利器 十二.文件及IO 当在输入输出流中使用控制符进行格式控制时,需在程序中加入头文件 ...

  2. 5G多输入多输出技术,到底是个啥东东?

    摘要:多输入多输出技术是指在发射端和接收端分别使用多个发射天线和接收天线,使信号通过发射端与接收端的多个天线传送和接收,从而改善通信质量. 本文作者|历天一 多输入多输出技术是指在发射端和接收端分别使 ...

  3. 几款Java开发者必备常用的工具,准点下班不在话下

    摘要:一问一答的形式轻松学习掌握java工具. 以一问一答的形式学习java工具 Q:检查内存泄露的工具有?A: jmap生成dump转储文件,jhat可视化查看. Q:某进程CPU使用率一直占满,用 ...

  4. 聊聊LiteOS事件模块的结构体、初始化及常用操作

    摘要:本文通过分析LiteOS事件模块的源码,深入掌握事件的使用. 事件(Event)是一种任务间通信的机制,可用于任务间的同步.多任务环境下,任务之间往往需要同步操作,一个等待即是一个同步.事件可以 ...

  5. LiteOS内核源码分析:消息队列Queue

    摘要:本文通过分析LiteOS队列模块的源码,掌握队列使用上的差异. 队列(Queue)是一种常用于任务间通信的数据结构.任务能够从队列里面读取消息,当队列中的消息为空时,挂起读取任务:当队列中有新消 ...

  6. MongoDB 读写分离——SpringBoot读写分离

    application.yml data: mongodb: uri: mongodb://127.0.0.1:27017,127.0.0.1:27018,127.0.0.1:27019/fecg_d ...

  7. Nginx 四层代理配置

    四层代理比较方便.简单,nginx.conf 如下 #user nobody; worker_processes 1; #error_log logs/error.log; #error_log lo ...

  8. CVPR2022 前沿研究成果解读:基于生成对抗网络的深度感知人脸重演算法

    凭借在人脸生成领域的扎实积累和前沿创新,阿里云视频云与香港科技大学合作的最新研究成果<基于生成对抗网络的深度感知人脸重演算法 >(Depth-Aware Generative Advers ...

  9. C#写日志工具类(新版)

    源码:https://gitee.com/s0611163/LogUtil 昨天打算把我以前写的一个C#写日志工具类放到GitHub上,却发现了一个BUG,当然,已经修复了. 然后写Demo对比了NL ...

  10. Mina Tcp服务器开发

    因项目架构需求,需要开发一个Mina Tcp服务器.我的Mina服务器是Java winForm,这与在web项目使用会有少许不同. 1.Maven依赖 <dependency> < ...