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 ...
随机推荐
- 安卓fragment transaction add方法报错
这个问题百度了很多能用的很少! 原来看的B站的视频教程比较老了参数不匹配!我记一下安卓studio3.1的方法 切换fragment 前都先要 FragmentManager manager=getS ...
- mysql8 安装&问题解决
1.下载:https://dev.mysql.com/downloads/mysql/ 2.安装 1).设置环境变量 MYSQL_HOME D:\env\j2ee\mysql\mysql-8.0.19 ...
- Day 30:HTML和CSS在Java项目中常用语法
framSet例子,其中的页面链接地址视情况而定,应为我还不知怎么弄当前文件下呢,例子主要在说明该标签如何使用 <!DOCTYPE html PUBLIC "-//W3C//DTD X ...
- Golang的进制转换实战案例
Golang的进制转换实战案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.常用进制概述 1>.进制概述 进制也就是进位制,是人们规定的一种进位方法.举个例子:二进制就 ...
- 140-PHP类的抽象方法和继承
<?php abstract class father{ //定义一个抽象类 abstract public function test(); //定义抽象方法 } class son exte ...
- List实体类、DataTable与xml互转
序列化常用Attribute讲解说明 [XmlRootAttribute("MyCity", Namespace="abc.abc", IsNullable=f ...
- C# Stream篇(二) -- TextReader 和StreamReader
TextReader 和StreamReader 目录: 为什么要介绍 TextReader? TextReader的常用属性和方法 TextReader 示例 从StreamReader想到多态 简 ...
- kubernter相关内容
1. Kubernetes 第一章:互联网架构的演变 随着1946年世界上第一台电子计算机的问世网络就随之出现了,只不过当初只是为了解决多个终端之间的连接,这就是局域网的雏形.后来,随着美国国防部高级 ...
- Egret Engine 2D - 项目配置
todo 看三个示例项目的完整源码和资源 <e:Group name="Button" height = "300" verticalCent ...
- 17 ~ express ~ 分类的显示 ,修改 和 删除
一,前台显示 /views/admin/category.html {% extends 'layout.html' %} {% block main %} <ol class="br ...