Compilation_Principle_001_编译和链接

本文详细介绍了C语言的编译过程,包括预处理、编译、汇编和链接。重点讲解了静态链接库和动态链接库的概念及其实现方式,是理解编译原理的好助手。
摘要由CSDN通过智能技术生成

写在前面: 该文章一部分参考xxx大学计算机老师的文章, 如有侵犯版权, 请联系删除

编译过程
源代码到可执行程序经历的过程.

(源代码(hello.c) + 头文件(stdio.h))
    ↓
预处理(cpp) -> hello.i
                 ↓
            编译(ccl) -> hello.s
                            ↓
                        汇编(as) -> hello.o 
                                        ↓
                                    链接器(ld) <- (静态链接库(libc.a))
                                        ↓
                                    可执行文件(hello)
1. 预处理
预处理指令: gcc -E hello.c -o hello.i
预处理完成以下事情:
    1. 展开所有的宏定义并删除 #define
    2. 处理所有的条件编译指令, 例如 #if #else #endif #ifndef ...
    3. 展开头文件, 即将#include替换为头文件实际内容, 递归进行
    4. 把所有的注释 // 和 / / 替换为空格
    5. 添加行号和文件名标识以供编译器使用
    6. 保留所有的 #pragma 指令, 因为编译器要使用
2. 编译
编译指令: gcc -S hello.i -o hello.s
编译完成以下事情:
    把预处理之后的文件进行一系列词法分析, 语法分析, 语义分析以及优化后生成相应的汇编代码文件. 
3. 汇编
汇编指令: gcc -c hello.s -o hello.o
汇编完成以下事情:
    就是将编译生成的汇编代码翻译为机器码, 几乎每一条汇编指令对应一句机器码.

Note: 不同操作系统之间的可执行文件的格式通常是不一样的, 所以造成编译好的 hello 可执行文件没办法直接
复制执行, 而需要在相关平台上重新编译, 当然, 不能运行的原因自然不是这一点, 不同的操作系统接口(windows API 和 
Linux System Call)以及相关的类库也是原因之一. 
4. 链接
链接指令: gcc hello.o -o hello (编译器默认链接C标准库)
链接完成以下事情:
    将各种代码和数据部分收集起来并组合成为一个单一文件的过程, 这个文件可被加载(或被拷贝)到存储器并执行.

Note: 链接可以执行于编译时, 也就是在源代码被翻译成机器代码时; 也可以执行于加载时, 也就是在程序被加载器加载到
存储器并执行时; 甚至执行于运行时, 由应用程序来执行. 

其实, 任何一个程序, 它的背后都有一套庞大的代码在支撑着它, 以使得该程序能正常运行. 
这套代码至少包含入口函数, 以及其所依赖的函数构成的函数集合. 当然 , 它还包含了各种 标准库函数的实现. 这个"支撑模块"就叫做运行时库(Runtime Library). 而C语言的运行时库, 被称为C运行时库(CRT).

CRT大致包括: 启动与退出相关的代码(包括入口函数及入口函数所依赖的其它函数), 标准库函数(ANSI C标准规定的函数实现)(如: printf, scanf函数都是标准库函数), I/O相关, 堆的封装实现, 语言特殊功能的实现以及调试相关.
4.1 静态链接库
我们几乎每次写程序都难免使用库函数, 而库函数是提前编译好的, 需要的时候直接链接, 那么, 标准库是以什么形式存在? 一个目标文件? 我们知道, 链接的最小单位是一个个目标文件, 如果我们只在程序中使用标准库中的printf函数, 就需要和真个库链接的话岂不是太浪费资源, 但是, 如果把库函数分别定义在彼此独立的代码文件中, 编译出来则是一大堆目标文件, 那也太混乱了吧? 所以, 编译器系统提供了一种机制, 将所有编译出来的目标文件打包成一个单独的文件, 叫做静态库(static library).

Note: Linux/Unix系统下ANSI C的库名为libc.a, 数学函数单独在libm.a库中. 静态库采用一种称为存档(archive)的特殊文件格式保存. 其实就是一个目标文件的集合, 文件头描述了每个成员目标文件的位置和大小. 当链接器和静态库链接时, 链接器会从这个打包文件中"解压缩"出需要的部分目标文件进行链接. 这样也就解决了资源浪费的问题.

4.1.1 静态库简单实现

//在swap.c 中定义swap函数, 在add.c中定义add函数, 再添加个包含它们的calc.h文件
//swap.c

void swap(int *num1, int *num2) 
{
    int temp = *num1;
    *num1 = *num2;
    *num2 = temp;
}

//add.c

int add(int a, int b)
{
    return a + b;
}

//calc.h

#ifndef CALC_H_
#define CALC_H_

#ifdef _cplusplus

extern "C" {
#endif 

void swap(int *, int *);
int add(int, int);

#ifdef _cpluscplus    
}

#endif

#endif
Step1. 分别编译它们得到 swap.o 和 add.o 这两个目标文件, 再使用 ar 命令将其打包称为一个静态库. 
    1. gcc add.c -c -o add.o
    2. gcc swap.c -c -o swap.o
    3. ar rcs libcalc.a swap.o add.o
 Step 2. 使用静态库
 //编写test.c 使用这个库中的swap函数
 //test.c

 #include <stdio.h>
 #include <stdlib.h>

 #include "calc.h"

 int main(int argc, char *argv[])
 {
     int a = 1, b = 2;
     swap(&a, &b);
     printf("a= %d, b = %d \n", a, b);

     return 0;
 }
Step 3. 编译执行test.c
    1. gcc test.c ./libcalc.a -o test
    2. ./test

Note: 使用C语言标准库时, 编译器会默认进行链接, 不过因为数学函数库libm.a没有默认链接, 所以如果我们使用了数学函数, 在编译时需要在命令行指定 -lm 链接(-l 是指定链接库, m是去掉lib之后的库名), 不过目前新版本的gcc都默认链接libm.c库了. 

静态库缺点: 
    1. 每一个使用了相同的C标准函数的程序都需要和相关目标文件进行链接, 浪费磁盘空间.
    2. 当一个程序有多个副本执行时, 相同的库代码部分被载入内存, 浪费内存.
    3. 当库代码更新之后, 使用这些库的函数必须全部重新编译.
4.2 动态链接库(也称共享库(shared library))
动态链接库/共享库是一个目标模块, 在运行时可以加载到任意的存储器地址, 并和一个正在运行的程序链接起来. 这个过程就是动态链接(dynamic linking), 是由一个叫动态链接器(dynamic linker)的程序完成的. 

Note: Linux/Unix中共享库的后缀名通常是.so, windows的共享库后缀名是.dll.

4.2.1 动态库简单实现

step 1. 首先建立动态库, 删除上面生成的静态库和目标文件, 执行以下指令即可生成动态库, 就是这么简单. 
    gcc swap.c add.c -shared -o libcalc.so

step 2. 使用静态库, 使用以下命令
    1. gcc test.c -o test ./libcalc.so
    2. ./test

Note: 使用 ldd 命令查看test文件的依赖, 得知该文件能运行需要依赖libcalc.so这个动态库, 同时也能看到C标准库也是动态链接的(在gcc编译的命令行上加 -static可以要求静态链接)   
动态库的优点:
    1. 库更新之后, 只需要替换掉动态库文件即可, 无需编译所有依赖库的目标文件.
    2. 程序有多个副本执行时, 内存中只需要一份库代码, 节省空间. 
基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip 个人大四的毕业设计、课程设计、作业、经导师指导并认可通过的高分设计项目,评审平均分达96.5分。主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。 [资源说明] 不懂运行,下载完可以私聊问,可远程教学 该资源内项目源码是个人的毕设或者课设、作业,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96.5分,放心下载使用! 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),供学习参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值