######################################## 编译期 ######################################## 代码编译过程 **************************************** 代码编译过程可以分为以下几个阶段: +----------+--------------------+------+--------------------+----------+ | 编译阶段 | 功能 | 产物 | 产生目标产物的命令 | 执行程序 | +==========+====================+======+====================+==========+ | 预编译 | 展开宏、删除注释 | .i | gcc -E | cpp | +----------+--------------------+------+--------------------+----------+ | 编译 | 生成汇编代码 | .s | gcc -S -masm=intel | ccl | +----------+--------------------+------+--------------------+----------+ | 汇编 | 生成目标文件 | .o | g++ -c -o | as | +----------+--------------------+------+--------------------+----------+ | 链接 | 对目标文件进行链接 | .out | gcc -o | ld | +----------+--------------------+------+--------------------+----------+ 预编译阶段不进行语法解析,只进行简单的宏展开 使用 objdump -d 可以反编译一个二进制文件 .. seealso:: - `Compiler Explorer `_ 位置独立代码 [#]_ **************************************** 所谓 :abbr:`位置独立代码 (Position Independent Code)` 可以理解为代码无绝对跳转,跳转都为相对跳转。生成动态库时,需要加上 -fPIC 选项。 添加 -fPIC 选项生成的动态库,是位置无关的。这样的代码本身就能被放到线性地址空间的任意位置,无需修改就能正确执行。动态库在内存中只需要加载一次,而可以被映射到多个进程的虚拟空间中。 实际上,即使是不加 -fPIC 选项也可以生成动态库,但是这要求动态库 **不能与引用其他代码** (这里我认为实际上是不能引用其他动态库中的函数) 不添加 -fPIC 生成的动态库,生成时假定它被加载在地址 0 处。加载时它会被加载到一个地址 (base),需要进行一次重定位,代码、数据段中所有的地址加上这个 base 的值。这时代码运行时就能使用正确的地址了。 位置相关的动态库在每次被引用时都会生成一份新的副本,但是由于不需要进行内存映射,因此加载速度比较快 静态库、动态库和小库 [#]_ **************************************** 在解释静态库和动态库之前,需要简单了解一下什么是目标文件。目标文件常常按照特定格式来组织,在linux下,它是 :abbr:`ELF (Executable Linkable Format)` ,而在 Windows 下是 :abbr:`PE (Portable Executable)` 。 目标文件有三种: - 可执行目标文件。即我们通常所认识的,可直接运行的二进制文件。 - 可重定位目标文件。包含了二进制的代码和数据,可以与其他可重定位目标文件合并,并创建一个可执行目标文件。 - 共享目标文件。它是一种在加载或者运行时进行链接的特殊可重定位目标文件。 将第二种目标文件以一种特定的方式打包成一个单独的文件,并且在链接生成可执行文件时,从这个单独的文件中“拷贝”它自己需要的内容到最终的可执行文件中。这个单独的文件,称为静态库。Linux 中通常以 .a (archive) 为后缀。而 Windows 下以 .lib (library) 结尾。 - 静态库使用 *ar* 创建:[#]_ .. code-block:: bash g++ -c math.cpp ar -crv libmath.a math.o - 静态库使用 *static* 链接: .. code-block:: bash gcc -static -o main main.o -lmath 这里 *-lmath* 需要放在后面,否则的话编译期在扫描的时候先扫描前面的库文件,后面的目标文件就没办法找到需要的函数了。 动态库和静态库类似,但是它并不在链接时将需要的二进制代码都“拷贝”到可执行文件中,而是仅仅添加一些重定位和符号表信息用来帮助程序在运行时加载二进制代码。Linux中通常以 .so(Shared Object)作为后缀。Windows 下以 .dll (Dynamic Link Library) 结尾。 .. note:: 动态库不拷贝所有信息不代表不需要信息,编译时链接动态库依然需要拷贝必要的信息,这部分信息在 Linux 下由动态库文件提供,在 Windows 由导入库(.lib)提供。 - 动态库使用 *shared* 创建 .. code-block:: bash g++ -fPIC -shared math.cpp -o libmath.so - 使用动态库 链接时默认就是使用动态库,无需任何指定 .. code-block:: bash g++ main.cpp -lmath 动态库的需要放在以下位置之一操作系统才能找到: - 可执行文件同路径 - 系统默认库路径(/usr/lib 或 C:/windows/) :abbr:`小库 (Little libraries)` : 小库与动态库类似,在运行时加载,但是编译时不需要任何额外信息,代价是需要手动装入库和解析库函数。 小库使用 *dlopen* 、 *dlclose* 、*dlsym* 用来打开库、关闭库、解析库。[#]_ 小库使用 **-ldl** 编译 .. [#] `osition-independent code (PIC) 编译动态库 `_ .. [#] `浅谈静态库和动态库 `_ .. [#] `C++静态库与动态库 `_ .. [#] `gcc -ldl 选项作用 `_ 静态库 **************************************** 下面描述如何手动创建静态库: #. 创建源文件 .. code-block:: cpp // FUNC.cpp #include void FUNC(void){ std::cout<<"FUNC lib was called"<`_ 二进制构成 **************************************** C++ 二进制文件由五部分组成: [#binary]_ - :abbr:`BSS (Block Started by Symbol)` 用来存放程序中未初始化的全局变量的一块内存区域 - 数据段:通常是指用来存放程序中已初始化的全局变量和静态变量的一块内存区域 - 代码段:用来存放程序执行代码的一块内存区域,通常只读,也有可能包含一些只读的常数变量,例如字符串常量等 - 堆:于存放进程运行中被动态分配的内存段 - 栈:用户存放程序临时创建的局部变量 .. [#binary] `C++ BSS段、数据段、代码段、堆与栈及五大内存分区 `_ 段错误 **************************************** 段错误一般可能由以下情况引起: - 使用了空指针 - 数组越界 - 迭代器失效 - 迭代太深导致栈溢出