C/C++源程序到可执行程序的过程
源程序.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++源程序到可执行程序的过程的更多相关文章
- 转载:C/C++源代码到可执行程序的过程详解
C/C++源代码到可执行程序的过程详解 编译,编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格 ...
- 串口调试助手vc源程序及其详细编写过程
串口调试助手vc源程序及其详细编写过程 目次: 1.建立项目 2.在项目中插入MSComm控件 3.利用ClassWizard定义CMSComm类控制变量 4.在对话框中添加控件 5.添加串口事件 ...
- C/C++源代码到可执行程序的过程详解
编译,编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格式的要求链接生成可执行程序. 源代码-- ...
- C代码编译成可执行程序的过程
C代码通过编译器编译成可执行代码,经历了四个阶段,依次为:预处理.编译.汇编.链接. 接下来详细讲解各个阶段 一.预处理 1.任务:进行宏定义展开.头文件展开.条件编译,不检查语法. 2.命令:gcc ...
- C中的预编译宏定义
可以用宏判断是否为ARC环境 #if _has_feature(objc_arc) #else //MRC #endif C中的预编译宏定义 -- 作者: infobillows 来源:网络 在将一 ...
- C预编译, 预处理, C/C++头文件, 编译控制,
在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的 ...
- linux装载可执行程序简析
朱宇轲 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 linux中主要 ...
- 对于Linux内核执行过程的理解(基于fork、execve、schedule等函数)
382 + 原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/ 一.实验环境 win10 -> VMware -> Ubuntu1 ...
- Linux进程启动过程分析do_execve(可执行程序的加载和运行)---Linux进程的管理与调度(十一)
execve系统调用 execve系统调用 我们前面提到了, fork, vfork等复制出来的进程是父进程的一个副本, 那么如何我们想加载新的程序, 可以通过execve来加载和启动新的程序. x8 ...
随机推荐
- JAVA Random 详解
Java中存在着两种Random函数: 一.java.lang.Math.Random; 调用这个Math.Random()函数能够返回带正号的double值,该值大于等于0.0且小于1.0,即取值范 ...
- kali linux终端快捷键设置
kali里面是没有终端快捷键的,需要自己设置. 打开设置->设备->keyboard,拉到最下面点击加号来新建一个快捷键. 名称:打开终端 命令:gnome-terminal 快捷键:Ct ...
- Spark技术学院-进去能学到啥?
Spark技术学院是什么? 主要是浪尖,前腾讯现阿里的大神一起搞的知识分享基地,旨在帮助大家由入门到精通spark,hbase,kafka大数据重要的框架,还有给入门小白指点入门方法,分享入门资料,对 ...
- scan port
$sudo apt-get install nmap $nmap 127.0.0.1 Starting Nmap 7.60 ( https://nmap.org ) at 2020-02-20 15: ...
- PHP页面跳转以及伪登录实例
PHP页面跳转一.header()函数header()函数是PHP中进行页面跳转的一种十分简单的方法.header()函数的主要功能是将HTTP协议标头(header)输出到浏览器. header() ...
- 杭电oj3306:Another kind of Fibonacci(矩阵快速幂)
Another kind of Fibonacci 题目链接 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (J ...
- java核心-多线程(1)-知识大纲
Thread,整理一份多线程知识大纲,大写意 1.概念介绍 线程 进程 并发 2.基础知识介绍 Java线程类 Thread 静态方法&实例方法 Runnable Callable Futur ...
- Spark 2.x Troubleshooting Guide
IBM在spark summit上分享的内容,包括编译spark源码,运行spark时候常见问题(缺包.OOM.GC问题.hdfs数据分布不均匀等),spark任务堆/thread dump 目录 编 ...
- java基础源码 (2)--StringBuilder类
Serializable(接口): 是一个IO的序列化接口,实现了这个接口,就代表这个类可以序列化或者反序列化,该接口没有方法或者字段,仅用于标识可串行话的语义. Appendable(接口): /* ...
- js如何操作或是更改sass里的变量
/*上网搜索了好多方法,最终只有这一种比较适合*/ 参考: https://blog.csdn.net/weixin_44392565/article/details/85755592 https:/ ...