C++:5.编译加载过程

C++:5.编译加载过程 目录1.预编译2.编译3.汇编4.链接5.加载/运行6.一键编译指令C代码从编译到最终执行中间大致分为5个步骤预编译、编译、汇编、链接、运行。1.预编译预编译又叫预处理。执行指令过程为g -E a.cpp -o a.ii。这个过程主要做的事情有1.清理代码注释。用// 或/* */注释的代码都会被清空。2.替换宏定义。比如 #define PI 3.14代码中使用了这个宏如 double value PI。那么预编译时候PI会被直接替换为3.14即double value 3.14。3.插入include引入的文件内容把引入头文件的所有内容直接复制粘贴到当前文件中。所以a.ii通常会比a.cpp大不少。4.清理条件编译代码条件编译关键字有#ifdef、#ifndef、#if、 #else、#elif 、#endif会按具体条件逻辑保留部分代码删除另外部分代码。5.添加行号便于debug调试使用或报错时定位哪一行代码。2.编译执行指令过程为g -S a.ii -o a.s。主要用来对预编译后的文件代码进一步检查语法、语义、词法等是否正确。比如变量类型错误、缺少“}”、缺少“”等等都是在这里检查。此外编译还会对代码进行优化比如int a46。编译时候会被改为int a10。3.汇编执行指令过程为g -C a.s -o a.o。主要用来将编译生成的汇编代码转换为二进制机器指令。不通平台或系统这里生成的肯定是不一样的。目标文件格式Windows下是.objlinux下是.o。汇编在生产二进制指令时还会做一个重要事情就是生成符号表。那什么是符号表呢顾名思义符号表存储的就是一堆符号。而符号指的就是代码中的变量、函数名、全局常量等。符合有很多属性包括名称、类型、作用域、地址、行号等。比如debug时候生成的可执行文件比较大就是因为里面包含了符合表信息。在debug时候能看到函数调用关系也能知道哪一行报错了。如果没有符合表比如release版本保存时候看到的都是各种16进制地址。4.链接执行指令过程为g a.o b.o -o main。主要用来将目标文件、系统库、第三方库.o/.a/.so文件连接起来生成一个可执行的文件。这里面又可以细分为几个功能。1.合并段主要用来合并各个目标文件的代码段 (.text)、数据段 (.data/.bss)统一布局内存地址。可以用如下命令查看可执行文件中各个段的情况。readelf -S main查看结果如下。里面的代码段.text和数据段.data和.bss都能看到。2.符合解析匹配引用符号函数 / 全局变量和真实符号定义3.重定位修正代码里的地址占位符把外部函数调用、全局变量访问地址改成真实内存偏移地址4.库链接静态链接.a/.lib把库直接切入到可执行程序中 。下次可以独立运行不需要额外依赖库。缺点就是体积比较大。动态链接.so/.dll/.dylib体积小但运行时必须有额外配套的依赖库本身独立运行不了。5.加载/运行当执行命令./main时程序开始运行。中间可以细分为如下几个过程。1.创建进程虚拟地址空间2.将可执行文件的代码段、数据段映射等载入进程虚拟内存3.处理动态链接动态链接器ld-linux.so/msvcrt.dll 等加载依赖的动态库完成运行时符号重定位。4.初始化全局变量、静态变量、初始化栈 、堆等5.设置程序入口不是直接 main 函数是系统启动函数 _start / CRT 运行时库入口6.一键编译指令直接用 -o 命令一步生成可执行文件。gcc main.c -o mian