源程序.cpp  预处理得到

预处理文件.i   编译得到

汇编文件.S    汇编得到

目标文件.o     链接得到

可执行文件

例子:main.cpp  fun.cpp fun.h

 #include <iostream>
#include "fun.h"
using namespace std; #define PI 3.14 int main()
{
print();
cout<<PI<<endl;
return ;
}
 #ifndef _FUN_H_
#define _FUN_H_
void print();
#endif
 #include <iostream>
#include "fun.h"
void print()
{
std::cout<<"hello,world"<<std::endl;
}

1. 预处理

g++ -E main.cpp -o main.i

main.i、fun.i:

      

  

  对源程序其中的伪指令(以#开头的指令)和特殊符号进行处理

(1)宏定义指令

  如 main.cpp中有 #define PI 3.14,预处理之后进行了替换

(2)条件编译指令

  #ifdef、#ifndef、#else、#elif、#endif等,根据宏定义决定对哪些代码进行处理,避免重复的引用

(3)头文件包含指令

  #include <xx.h>   #include "xx.h"等

  这些头文件中有大量的宏定义

(4)特殊符号

 printf("Date:%s,Time:%s,File:%s,Line:%d,Func:%s\n",__DATE__,__TIME__,__FILE__,__LINE__,__FUNCTION__);

  

  经过预处理,得到的.i文件没有宏定义、没有条件编译指令、没有特殊符号

2.  编译

g++ -S main.i -o main.S

  

  预处理之后的文件只有一些数字、字符串及关键字的定义,经过g++编译程序:词法分析、语法分析、优化,生成汇编文件

3. 汇编

  汇编代码汇编成机器指令

4. 链接

  多个.o文件以及库文件链接成可执行文件

  ld 一堆库文件 fun.o main.o -o a.out

  必要的库可通过  g++ -v main.o 查看

  g++ 最终通过调用 collect2来链接文件,collect2是对ld的封装

(1)静态链接

  以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件。

  将链接库的代码复制到可执行程序中

  静态链接做的事:

    ①符号解析:将目标文件符号引用和定义联系起来(因为某些符号是引用其他模块的符号)

    ②重定位:编译器、汇编器生成从地址0开始的代码和数据,链接器把每个符号定义和一个存储器位置联系起来,然后修改所有对这些符号的引用,使得从另一个位置开始执行。

(2)动态链接

  函数的定义在动态链接库或共享对象的目标文件中,在链接阶段,动态链接库只提供符号表等少量信息保证所有符号引用都有定义(不像静态链接直接复制过去),保证编译顺利通过。在可执行文件执行时,动态连接库将函数等内容映射到运行时相应进程的虚地址空间。

(3)目标文件

  ①可重定位目标文件:含二进制代码、数据,因引用了其他模块的符号而不能执行

  ②共享目标文件/动态库: .so文件

  ③可执行文件

(4)目标文件的格式 ELF文件

  ELF头:描述文件系统字长、字节序、ELF头大小、目标文件类型、目标机类型等

  .text:代码段,可执行二进制机器指令

  .rodata:只读数据段,存常量如字符串等

  .data:数据段,以明确初始化的全局数据(全局变量、静态变量),是静态内存分配

  .bss:块存储段,未被明确初始化的全局数据,这些全局数据会初始化为0,是静态内存分配

  上面的四个段会加载到内存中

  .symtab:符号表,定义和引用的函数和全局变量

  .rel.text:代码段需要重定位的信息,存储需要靠重定位修改位置的符号的汇总

  .rel.data:数据段需要重定位的信息

  .debug:gcc -g选项会生成此段

  .line:源程序的行号映射  用于调试

  .strtab:字符串表存储symtab、debug符号表中符号的名字

  查看ELF文件内容、各段大小的命令:

 readelf -a main
2 size main

  gcc命令基本选项:

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

库的生成与使用:

(1)静态库

ar rcs fun.a fun1.o fun2.o 

  选项:r:把列表中的目标文件加入到静态库

        c:若指定的静态库不存在则创建该文件

        s:更新静态文件的索引,使之包含新加入的目标文件的内容

链接时:

gcc main.c -lfun.a -o main
gcc -L. main.c -o main

  -L紧跟静态库路径

(2)动态库

gcc -shared -fPIC -o lib.so lib,c

  选项的含义:

    -shared:生成动态库

    -fPIC:生成位置无关代码

链接时:

gcc main.c ./lib.so -o main

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

可执行文件在运行时:

  除了代码段、数据段、BSS段,还有堆区和栈区

  堆区:用于动态分配内存,用 malloc、free申请和释放

              从低地址向高地址增长

              链式存储

效率比栈低

  栈区:由操作系统自动分配和释放,存储函数的参数值、局部变量的值等

从高地址向低地址增长

        连续内存

最大容量固定

C/C++源程序到可执行程序的过程的更多相关文章

  1. 转载:C/C++源代码到可执行程序的过程详解

    C/C++源代码到可执行程序的过程详解 编译,编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格 ...

  2. 串口调试助手vc源程序及其详细编写过程

    串口调试助手vc源程序及其详细编写过程   目次: 1.建立项目 2.在项目中插入MSComm控件 3.利用ClassWizard定义CMSComm类控制变量 4.在对话框中添加控件 5.添加串口事件 ...

  3. C/C++源代码到可执行程序的过程详解

    编译,编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格式的要求链接生成可执行程序. 源代码-- ...

  4. C代码编译成可执行程序的过程

    C代码通过编译器编译成可执行代码,经历了四个阶段,依次为:预处理.编译.汇编.链接. 接下来详细讲解各个阶段 一.预处理 1.任务:进行宏定义展开.头文件展开.条件编译,不检查语法. 2.命令:gcc ...

  5. C中的预编译宏定义

     可以用宏判断是否为ARC环境 #if _has_feature(objc_arc) #else //MRC #endif C中的预编译宏定义 -- 作者: infobillows 来源:网络 在将一 ...

  6. C预编译, 预处理, C/C++头文件, 编译控制,

    在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的 ...

  7. linux装载可执行程序简析

    朱宇轲 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 linux中主要 ...

  8. 对于Linux内核执行过程的理解(基于fork、execve、schedule等函数)

    382 + 原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/ 一.实验环境 win10 -> VMware -> Ubuntu1 ...

  9. Linux进程启动过程分析do_execve(可执行程序的加载和运行)---Linux进程的管理与调度(十一)

    execve系统调用 execve系统调用 我们前面提到了, fork, vfork等复制出来的进程是父进程的一个副本, 那么如何我们想加载新的程序, 可以通过execve来加载和启动新的程序. x8 ...

随机推荐

  1. leetcode746 Min Cost Climbing Stairs

    """ On a staircase, the i-th step has some non-negative cost cost[i] assigned (0 inde ...

  2. Problem J. Joseph’s Problem 约瑟夫问题--余数之和

    链接:https://vjudge.net/problem/UVA-1363 题意:给出n  k,当 i 属于 1~n 时 ,求解 n% i 的和 n 和 k 的范围都是 1 到 10^9; 商相同 ...

  3. Eclipse新建Maven中创建src文件夹报The folder is already a source folder错误解决办法

    问题: 解决办法:右击项目->Build Path->Configure Build Path选择(missing)文件夹remove,然后重新New Source Folder

  4. Hash!

    Panda一个字符串是否是另一个字符串的子串 #include<bits/stdc++.h> using namespace std; const int mod=998244353,tt ...

  5. cf 782# A.Andryusha and Socks B.The Meeting Place Cannot Be Changed C.Andryusha and Colored Balloons

    看来快掉到灰名的蒟蒻涨rating也快... A题模拟一下就好(一开始还sb,, #include<bits/stdc++.h> #define LL long long using na ...

  6. python面试题整理(一)

    python基础:1.列表生成式和生成器表达式有什么区别 我说的是首先写法不一样,列表生成式用[],生成器表达式用(),其次列表生成是一次性生成一个完整的列表,生成器表达式返回的是一个一个的值,占用内 ...

  7. 17.swoole学习笔记--异步mysql操作

    <?php //异步mysql操作 $db=new swoole_mysql(); $config=[ 'host'=>'192.168.10.31', 'user'=>'zouke ...

  8. NumPy 数组创建

    章节 Numpy 介绍 Numpy 安装 NumPy ndarray NumPy 数据类型 NumPy 数组创建 NumPy 基于已有数据创建数组 NumPy 基于数值区间创建数组 NumPy 数组切 ...

  9. js基础学习之-js全局对象

    声明的三种方式: 第一种: var test; //或var test = 5; 第二种: test = 5; 第三种: window.test; //或window.test = 5; //只是使用 ...

  10. PAT Advanced 1072 Gas Station (30) [Dijkstra算法]

    题目 A gas station has to be built at such a location that the minimum distance between the station an ...