源程序.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. Irecycleview 的初次使用简单介绍(irecycleview 下拉刷新上拉加载)

    导包 还得加一个maven地址自己也看一下作者git把有详细解释(自己也要导入recycleview的包) 我的例子下载地址  https://www.lanzous.com/i32yzaj impl ...

  2. Java 日期与时间

    章节 Java 基础 Java 简介 Java 环境搭建 Java 基本语法 Java 注释 Java 变量 Java 数据类型 Java 字符串 Java 类型转换 Java 运算符 Java 字符 ...

  3. POJ 1330:Nearest Common Ancestors

    Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20940   Accept ...

  4. node.js - 定义全局变量

    1,定义全局变量 app.set('name','八戒') 2,获取全局变量 app.get('name')

  5. 微信小程序调用用百度地图天气功能

    #小程序之调用百度地图天气功能 本篇博客主要介绍小程序在百度地图中获取天气信息,如有不全请指出.下面先上效果图 主要内容 百度地图API的个人密钥,也就是AK 请求百度地图API接口数据 获取到的信息 ...

  6. python itertools 用法

    1.介绍itertools 是python的迭代器模块,itertools提供的工具相当高效且节省内存.使用这些工具,你将能够创建自己定制的迭代器用于高效率的循环.- 无限迭代器 itertools包 ...

  7. 八十三、SAP中的ALV创建之二,ALV相关的类型池定义

    一.与ALV相关的类型都是在TYPE-POOLS:SLIS中.我们来到SE11 二.常用的定义有fieldca和layout等,用于显示字段,和控制信息数据等. 三.我们以VBAK表为例,用ALV输出 ...

  8. 分享一个php加密字符串类。

    class base64{ /** * 加密字符串 * @access static * @param string $data 字符串 * @param string $key 加密key * @r ...

  9. Redis Sentinel 学习笔记

    转载出处: http://blog.csdn.net/lihao21 概述 Redis Sentinel 是用来实现 Redis 高可用的一套解决方案.Redis Sentinel 由两个部分组成:由 ...

  10. Python LMDB的使用

    在python中使用lmdb linux中,可以使用指令 pip install lmdb 安装lmdb包. ---- lmdb 数据库文件生成 增 改 删 查 1.生成一个空的lmdb数据库文件 # ...