美文网首页Flutter圈子iOS进阶之路iOS学习笔记
C语言底层原理(一):预处理、编译、汇编、链接

C语言底层原理(一):预处理、编译、汇编、链接

作者: 康小曹 | 来源:发表于2019-05-22 16:22 被阅读3次

本文的示例代码

文件层级:



其中,test.c代码:

// test.c
#include <stdio.h>
#include "mymath.h"// 自定义头文件
int main(){
    int a = 2;
    int b = 3;
    int sum = xkadd(a, b);
    printf("a=%d, b=%d, a+b=%d\n", a, b, sum);
}

mymath.h中声明相关方法:

// mymath.h
#ifndef MYMATH_H
#define MYMATH_H
int xkadd(int a, int b);
int xksum(int a, int b);
#endif

mymath.c中实现相关方法:

// mymath.c
int xkadd(int a, int b){
    return a+b;
}
int xksum(int a, int b){
    return a-b;
}

编译的整个过程

预处理:在编译之前由预处理器对源码进行的处理。主要包括宏替换、删除注释、执行预处理命令入#include、#ifdef等,生成.i文件。预处理后的文件会比源文件大很多,因为#include执行了复制,就<stdio.h>这个文件就有很多行了。此时文件仍然可以以文稿形式打开。

编译:将源码转化成汇编代码生成.s文件。汇编之后的.s文件都是汇编指令,比如movl、call等。此时文件仍然可以以文稿形式打开。

汇编:将汇编语言转化成机器码,生成.o文件。生成.o文件就是机器码了,不能使用文稿打开。

链接:编译之后的文件还不能直接执行,因为一个项目中存在多个文件,文件之间有引用关系,将这些文件进行关联并最终生成可执行程序的过程就是链接。

备注
宏义上的编译是指程序从源文件到二进制程序的全部过程,该过程包含预处理、编译、汇编、链接三个阶段。或者是从源文件到二进制文件的全过程,也就是生成.o文件的过程,该过程包含预处理、编译、汇编三个阶段。狭隘的编译就只是将.i文件转化成.s文件的过程。通常我们所说的编译就是宏义上的编译。

整个过程的流程图:


Compile Step

备注
有的博客中指出链接后有三种结果,分别是静态库、动态库、可执行文件。这种说法是错误的,动态库是通过gcc编译指令在链接之前生成,静态库的生成是通过ar指令进行打包,所以库是链接过程的参与文件而不是产物。

gcc指令

基本格式:gcc [options] file1 file2...  // 不加入参数时,默认依次执行编译、汇编、链接操作,并生成名为 a.out的可执行文件
常用参数:-E                 // 只执行预处理操作,生成.i文件
     -S                 // 只执行到编译操作为止,生成的是汇编文件(.s 或 .asm)
     -c                 // 只执行到汇编为止,不进行链接,生成.o的机器码文件
     -o filename        // 指定输出地址和文件名
     -static            // 静态链接
        -share            // 动态链接,也叫共享链接
     -g          // 编译时生成debug有关的程序信息(供gdb使用)
     --save-temps       // 保留中间文件,比如.i、.s、.o文件等
     -I PATH            // 在指定目录寻找引用文件
     -lname               // 链接库,其中name为去掉lib前缀和.so/.a后缀后的名字,-l后面不加空格,如-lmysqlclient就是链接libmysqlclient.a的静态库
     -L PATH            // 优先在指定路径下搜索链接的库文件,不设置时按照默认顺序寻址链接库

完整示例:

gcc –L /usr/dev/mysql/lib –static –lmysqlclient test.o –o test

拆解:
-o:将test.o文件以test文件名生成在当前路径
-L:在/usr/dev/mysql/lib下寻找相关的库文件
-l:mysqlclient在对应的路径下有libmysqlclient.so和libmysqlclient.a,使用-l(小写)指令时去掉lib和.so/.a后缀
–static:默认情况下, GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,加上-static选项,强制使用静态链接库。

静态库链接时搜索路径顺序

  1. ld会去找GCC命令中的参数-L
  2. 再找gcc的环境变量LIBRARY_PATH
  3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的

动态链接时、执行时搜索路径顺序:

  1. 编译目标代码时指定的动态库搜索路径
  2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
  3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
  4. 默认的动态库搜索路径/lib
  5. 默认的动态库搜索路径/usr/lib

有关环境变量:
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径

更多动态库、静态库知识请看:C语言底层原理(二):动态库、静态库

多文件编译

多文件编译的集中方法:
1、将多个文件编译成.o文件之后统一进行链接,并在链接时指定文件地址,示例:
文件夹层级:



指令:

gcc test.o ./inc/mymath.o -o a.out

2、将多个源文件直接使用gcc指令直接生成可执行程序
以本文最初提到的文件层级来执行以下指令:
执行指令:

 gcc test.c ./inc/mymath.c -I ./inc -o a.out

整个cmd过程:

caoxkdembp:C-Compile caoxk$ gcc test.c ./inc/mymath.c -o a.out
test.c:4:10: fatal error: 'mymath.h' file not found
#include "mymath.h"// 自定义头文件
         ^~~~~~~~~~
1 error generated.
caoxkdembp:C-Compile caoxk$ gcc test.c ./inc/mymath.c -I ./inc -o a.out
caoxkdembp:C-Compile caoxk$ ./a.out 
a=2, b=3, a+b=5
caoxkdembp:C-Compile caoxk$ 

3、如果要编译的文件都在同一个目录下,可以用通配符gcc *.c -o 来进行编译

makefile:文件描述了整个工程的编译、连接等规则。对于大型项目而言,可以编写makefile来实现自动编译整个项目中的文件,具体可以了解makefile的编写规则,此处不涉及。

更多文章

相关文章

网友评论

    本文标题:C语言底层原理(一):预处理、编译、汇编、链接

    本文链接:https://www.haomeiwen.com/subject/rivyzqtx.html