美文网首页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