• GCC编译过程


    一、Linux下GCC编译器编译过程

    序号过程名过程代码
    1预处理preprocessing将.c中的头文件,宏定义展开及替换,生成.i文件gcc -E -o hello.i hello.c
    2编译compilation将预处理之后的.i文件生成汇编代码.s文件gcc -S -o hello.s hello.i
    3汇编assembly把.s汇编文件生成.o目标文件gcc -c -o hello.o hello.s
    4链接linking将汇编生成的.o obj文件,系统库的obj文件,库文件链接起来,最终生成可以在特定平台运行的可执行程序gcc -o hello hello.o

    二、include 包含头文件

    1. #include <>
      【只能包含标准库的头文件.h】 用尖括号包含头文件,在系统指定的路径下找头文件。
    2. #include " "
      【不仅能包含标准库的头文件.h 多了一个在当前路径下找头文件】 用双引号包含头文件,先在当前目录下找头文件,找不到,再到系统指定的路径下找。

    三、define

     定义一个宏	      		#define P 3.14
     终止一个宏				#undef P 		(想在中间终止宏,后面遇到宏不可用)
     
     不带参宏             #define  P      3.1415
     带参宏               #define S(a,b)   (a)*(b)        (宏的形参没有类型)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    宏是在预编译的时候进行替换
    宏的作用范围:从定义的地方到本文末尾

    带参宏和带参函数的区别

    1.带参宏被调用多少次就会展开多少次,执行代码的时候没有函数调用过程,不需要压栈弹栈。
    所以带参宏,是浪费了空间,因为被展开多次,节省了时间。
    2.带参函数,代码只有一次,存在代码段,调用的时候去代码段取指令,调用的时候要压栈弹栈。有个调用的过程,带参函数是浪费了时间,节省了空间。
    3.带参函数的形参是有类型的,带参宏的形参是没有类型名
    
    • 1
    • 2
    • 3
    • 4

    四、选择性编译

    (1) ifdef
    如果在当前.c上定义过AA这个宏,就编译代码段1 ,否则编译代码段2

    #ifdef AA
        代码段1
    #else
        代码段2
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (2) ifndef
    如果在当前.c上没有定义过BB这个宏,就编译代码段1 ,否则编译代码段2

    #ifndef BB
        代码段1
    #else
        代码段2
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    一般用在#include"aa.h"中,避免头文件重复包含。
    在aa.h中
        #ifndef __FUN_AA__
        #define __FUN_AA__
        extern int funaa*(int x, int y);
        #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    五、静态库与动态库

    我们经常用到库函数,比如printf,strcpy
    实际上库函数都是由编译系统提供的,它会将库函数的源码对应的.c 编译成库文件,
    我们的代码中只需要调用这些库函数所在的库文件就可以了 (就是#include)。
    
    • 1
    • 2
    • 3
    1、什么是静态、动态编译?

    静态编译: 使用的是静态库文件进行编译
                      gcc -static hello.c -o hello
                      要把静态库文件打包编译到可执行程序中

    动态编译: 使用的是动态库文件进行编译(默认)
                      gcc hello.c -o hello
                      不会动态库文件打包编译到可执行程序中,只是编译链接关系
                      运行可执行程序时需要依赖静态库

    **静态链接:** 是由编译器在链接时将库的内容加入到可执行程序中的做法。
                      这里的库指的是静态链接库	(windows下以.lib为后缀,linux下以.a为后缀)
    
    **动态链接:** 把链接这个过程推迟到了运行时再进行,在可执行文件装载时或运行时,由操作系统的装载程序加载库。
                      这里的库指的是动态链接库(windows下以.dll为后缀,linux下以.so为后缀)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2、为什么要使用静态、动态编译?

    不想让别人看见内部接口的实现细节,只需要提供一个接口名和链接库文件。

    3、使用静态编译/动态编译

    文件1: mylibguo.c 定义 max()方法
    文件2: mylibguo.h 声明 max()方法
    文件3: myTest.c 主函数,调用max()方法

    文件1   mylibguo.c

    #include 
    int max(int a, int b)
    {
            if(a >= b)
                    return a;
            else
                    return b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    文件2   mylibguo.h

    #ifndef __MAX_H__
    #define __MAX_H__
    extern int max(int a, int b);
    #endif
    
    • 1
    • 2
    • 3
    • 4

    文件3   myTest.c

    #include
    #include "mylibguo.h"
    
    int main()
    {
      printf("this is main()\n");
      int num = 0;
      num = max(5, 9);
      printf("In main(), num=%d\n",num);
      return 0;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    正常编译

    gcc myTest.c mylibguo.c -o myTest
    ./myTest
    
    • 1
    • 2
    3.1、 使用静态编译

    (1)制作静态库   将mylibguo.c打包成库文件(.a)

    gcc -c mylibguo.c -o mylibguo.o
    ar rc libguo.a mylibguo.o
    
    • 1
    • 2

    生成的库文件需要以lib开头, .a结尾【此处为 libguo.a
    有了静态库文件后,编译文件就不需要知道main函数中调用的接口的具体实现了【也就是不需要mylibguo.c】,只需要调用接口的声明【mylibguo.h】和库文件 libguo.a

    (2)利用静态库编译程序  

    方法一: 库文件和main文件在同一个目录

    gcc -static myTest.c libguo.a -o myTest
    ./myTest
    
    • 1
    • 2

    结果:

    this is main()
    In main(), num=9
    
    • 1
    • 2

    方法二: 可以指定头文件及库文件的路径
    把头文件和库文件移动至/home_local/tester/suki/normalTest/1128/guo目录

    pwd
    /home_local/tester/suki/normalTest/1128
    ll
    total 24
    drwxrwxr-x 2 tester tester    6 Nov 28 02:20 guo
    -rw-rw-r-- 1 tester tester 1388 Nov 27 23:59 libguo.a
    -rw-rw-r-- 1 tester tester   75 Nov  3 23:53 mylibguo.h
    -rw-rw-r-- 1 tester tester  169 Nov 28 00:08 myTest.c
    
    mv libguo.a mylibguo.h guo/
    
    ll
    total 16
    drwxrwxr-x 2 tester tester   40 Nov 28 02:21 guo
    drwxrwxr-x 2 tester tester   42 Nov 28 00:06 move
    -rwxrwxr-x 1 tester tester 8632 Nov 28 00:10 myTest
    -rw-rw-r-- 1 tester tester  169 Nov 28 00:08 myTest.c
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    gcc -static myTest.c -o myTest -L/home_local/tester/suki/normalTest/1128/guo -lguo -I /home_local/tester/suki/normalTest/1128/guo
    ./myTest
    
    • 1
    • 2

    结果:

    this is main()
    In main(), num=9
    
    • 1
    • 2
    	-L 大写L		库文件的路径
    	-l 小写L		指定找哪个库,只要写库文件名,lib后 .a前的部分
    	-I 大写I		指定头文件的路径
    
    • 1
    • 2
    • 3

    方法三: 将库文件和头文件放在系统默认指定的路径下

    	库文件默认路径是    /lib  或 /usr/lib
    	头文件默认路径是    /usr/include
    	sudo mv libguo.a   /usr/lib
    	sudo mv mylibguo.h  /usr/include
    
    • 1
    • 2
    • 3
    • 4
    gcc -static myTest.c -o myTest -lguo
    ./myTest
    
    • 1
    • 2
    3.2、 使用动态编译

    (1)制作动态库   将mylibguo.c打包成库文件(.so)

    gcc -shared mylibguo.c -o libguo.so
    
    • 1

    生成的库文件需要以lib开头, .so结尾【此处为 libguo.so
    有了动态库文件后,编译文件就不需要知道main函数中调用的接口的具体实现了【也就是不需要mylibguo.c】,只需要调用接口的声明【mylibguo.h】和库文件 libguo.so

    (2)利用动态库编译程序  

    方法一: 库文件和main文件在同一个目录

    gcc myTest.c libguo.so -o myTest
    
    • 1

    A 可以看到已经生成了可执行文件

    ll
    -rwxrwxr-x 1 tester tester 8600 Nov 28 19:01 myTest
    
    • 1
    • 2

    B 执行可执行文件
    发现在加载动态库的时候出错了

    ./myTest
    ./myTest: error while loading shared libraries: libguo.so: cannot open shared object file: No such file or directory
    
    • 1
    • 2

    动态库只是建立链接关系,可执行程序运行的时候加载.so中的代码后,才去执行。执行时还需要依赖动态库,这个库不是一个标准的C库,并且还没有放在默认指定的路径下,所以在运行时系统还找不到,因此要添加环境变量,让系统知道,这个库在哪

    export LD_LIBRARY_PATH=./:SLD_LIBRARY_PATH
    
    • 1

    执行

    ./myTest
    
    • 1

    结果

    this is main()
    In main(), num=9
    
    • 1
    • 2

    方法二: 头文件及库文件假设在 /home_local/tester/suki/normalTest/1129/guo 目录

    gcc myTest.c -o myTest -L/home_local/tester/suki/normalTest/1129/guo -lguo -I/home_local/tester/suki/normalTest/1129/guo
    
    • 1

    执行,生成了myTest,说明编译成功,但是运行时出错

    ./myTest
    ./myTest: error while loading shared libraries: libguo.so: cannot open shared object file: No such file or directory
    
    • 1
    • 2

    因为编译时找到了库函数,但是链接时找不到库,
    需要执行以下操作,把链接库目录加入搜索路径

    export LD_LIBRARY_PATH=/home_local/tester/suki/normalTest/1129/guo:$LD_LIBRARY_PATH
    
    • 1

    执行

    ./myTest
    
    • 1

    结果

    this is main()
    In main(), num=9
    
    • 1
    • 2

    方法三: 库文件和main文件均在系统路径下

    cp libguo.so   /usr/lib
    cp mylibguo.h  /usr/include
    gcc myTest.c -o myTest -lguo
    ./myTest
    
    • 1
    • 2
    • 3
    • 4
    			我们前面的静态库也放在/usr/lib下了,那么连接的到底是静态库还是动态库呢
    
    • 1

    当静态库与动态库重名时,系统会优先连接动态库,或者通过加上-static来连接静态库。

  • 相关阅读:
    CMMI的五个级别
    FreeRTOS个人笔记-事件
    MyBatis-Plus 字段为Null时不更新解决方案,MyBatis-Plus 更新空字段
    数据采集-“消防知识网上答题挑战赛”题库
    前端动画的另一种方式 json动画
    使用stream下载文件避坑-》堆内存溢出的原因
    鸿蒙OS元服务开发说明:【WebGL网页图形库开发接口】
    【JAVA学习笔记】50 - Math类,Array类,System类,BigInteger和BigDecimal类
    Power Apps使用oData访问表数据并赋值前端
    VIM指令
  • 原文地址:https://blog.csdn.net/weixin_43787492/article/details/127998993