• 【Linux 系统】gcc/g++使用零星整理


    操作系统大致分为 2 大阵营,分别是 Windows 阵营和类 Unix 阵营(包括 Unix、Linux、Mac OS、安卓等)。通常情况下,Windows 系统下用户更习惯使用现有的 IDE 来编译程序;而类 Unix 系统下,用户更喜欢直接编写相应的 gcc 命令来编译程序。GNU 计划的实施可谓一波三折,最重要的一点是,虽然该计划为 GNU 操作系统量身定做了名为 Thr Hurd 的系统内核,但由于其性能比不上同时期诞生的 Linux 内核,最终 GNU 计划放弃 The Hurd 而选用 Linux 作为 GNU 操作系统的内核。在 Linux 内核的基础上,GNU 计划开发了很多系统部件,GCC 就是其中之一。

    sudo apt install gcc
    gcc --version
    
    • 1
    • 2
    1. gcc & g++
    1. GCC 编译器已经为我们提供了调用它的接口,对于 C 语言或者 C++ 程序,可以通过执行 gcc 或者 g++ 指令来调用 GCC 编译器。

    2. 只要是 GCC 支持编译的程序代码,都可以使用 gcc 命令完成编译。可以这样理解,gcc 是 GCC 编译器的通用编译指令,因为根据程序文件的后缀名,gcc 指令可以自行判断出当前程序所用编程语言的类别,比如:

    • xxx.c:默认以编译 C 语言程序的方式编译此文件;
    • xxx.cpp:默认以编译 C++ 程序的方式编译此文件。
    • xxx.m:默认以编译 Objective-C 程序的方式编译此文件;
    • xxx.go:默认以编译 Go 语言程序的方式编译此文件;
    1. 如果使用 g++ 指令,则无论目标文件的后缀名是什么,该指令都一律按照编译 C++ 代码的方式编译该文件。也就是说,对于 .c 文件来说,gcc 指令以 C 语言代码对待,而 g++ 指令会以 C++ 代码对待。但对于 .cpp 文件来说,gcc 和 g++ 都会以 C++ 代码的方式编译。

    2. 如果想使用 gcc 指令来编译执行 C++ 程序,需要在使用 gcc 指令时,手动为其添加 -lstdc++ -shared-libgcc 选项,表示 gcc 在编译 C++ 程序时可以链接必要的 C++ 标准库。

    3. g++ 指令就等同于 gcc -xc++ -lstdc++ -shared-libgcc 指令

    2. gcc std 编译标准
    GCC 版本C 语言常用标准
    C89/C90C99C11C17GNU90GNU99GNU11GNU17
    10.1 ~ 8.4c89 / c90c99c11c17/c18gnu90/gnu89gnu99gnu11gnu17/gnu18
    7.5 ~ 5.5c89/c90c99c11gnu90/gnu89gnu99gnu11
    4.9.4 ~ 4.8.5c89/c90c99c11gnu90/gnu89gnu99gnu11
    4.7.4c89/c90c99(部分支持)c11(部分支持)gnu90/gnu89gnu99(部分支持)gnu11(部分支持)
    4.6.4c89/c90c99(部分支持)c1x(部分支持)gnu90/gnu89gnu99(部分支持)gnu1x(部分支持)
    4.5.4c89/c90c99(部分支持)gnu90/gnu89gnu99(部分支持)
    GCC 版本C++ 常用标准
    C++98/03C++11C++14C++17GNU++98GNU++11GNU++14GNU++17
    10.1 ~ 8.4c++98/c++03c++11c++14c++17gnu++98/gnu++03gnu++11gnu++14gnu++17
    7.5 ~ 5.5c++98/c++03c++11c++14c++1z(部分支持)gnu++98/gnu++03gnu++11gnu++14gnu++1z(部分支持)
    4.9.4 ~ 4.8.5c++98/c++03c++11c++1y(部分支持)gnu++98/gnu++03gnu++11gnu++1y(部分支持)
    4.7.4c++98c++11(部分支持)gnu++98gnu++11(部分支持)
    4.6.4c++98c++0x(部分支持)gnu++98gnu++0x(部分支持)
    4.5.4c++98c++0x(部分支持)gnu++98gnu++0x(部分支持)
    3. gcc & g++ 编译程序案例
    gcc/g++ 指令选项功 能
    -E(大写)预处理指定的源文件,不进行编译。
    -S(大写)编译指定的源文件,但是不进行汇编。
    -c编译、汇编指定的源文件,但是不进行链接。
    -o指定生成文件的文件名
    -llibrary(-I library)其中 library 表示要搜索的库文件的名称。该选项用于手动指定链接环节中程序可以调用的库文件。建议 -l 和库文件名之间不使用空格,比如 -lstdc++。
    -ansi对于 C 语言程序来说,其等价于 -std=c90;对于 C++ 程序来说,其等价于 -std=c++98。
    -std=手动指令编程语言所遵循的标准,例如 c89、c90、c++98、c++11 等。
    .1. 单文件编译案例
    • c语言
    //存储在 demo.c 文件中
    #include 
    int main(){
       puts("GCC教程:http://c.xinbaoku.com/gcc/");
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    gcc demo.c -o a.out
    ./a.out
    
    • 1
    • 2
    • cpp
    //位于 demo.cpp 文件中
    #include 
    using namespace std;
    
    int main(){
        cout << "GCC教程:http://c.xinbaoku.com/gcc/" << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    g++ demo.cpp  #或者 gcc -xc++ -lstdc++ -shared-libgcc demo.cpp
    
    • 1
    .2. 预处理 -E
    • 展开所有的宏定义

    • 处理条件编译指令

    • 处理 include 预编译指令

    • 删除注释

    • 添加行号以及文件名标识

    • 但是会保留 #progma 编译器指令

    #include 
    int main(void){
        puts("hello world");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    #默认情况下 gcc -E 指令只会将预处理操作的结果输出到屏幕上,并不会自动保存到某个文件。因此该指令往往会和 -o 选项连用,将结果导入到指令的文件中
    
    # -C 选项,阻止 GCC 删除源文件和头文件中的注释
    gcc -E demo.c -o demo.i
    
    gcc -E -C demo.c -o demo.i
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    .3. 编译 -S
    • 进行词法分析,语法分析,语义分析

      • 词法分析 — 利用一个扫描器,将字符序列分割成一个个记号(例如:array 标识符,[ 左方括号)

      • 语法分析 — 产生语法树,检测语法

      • 语义分析 — 在语法树的基础上,加上类型,加隐式转换的节点,再次进行分析

    • 进行代码优化

      • 删除多余的运算
      • 代码外提,把可以在循环外面所执行的代码提出
      • 强度削弱,将强度大的运算换算成小的运算,例如将乘法转化为加法
      • 变换循环的控制条件,可以将原本不必要的变量直接在循环内部剔除
      • 合并已知量和复写传播,将已知量直接替换避免计算
      • 删除无用赋值
    • 产生相应的汇编代码源文件

    gcc -S demo.i -o test.i
    gcc -S demo.c -fverbose-asm  # 提高汇编代码的可读性
    
    • 1
    • 2
    .4. 汇编 -C
    • 将汇编代码源文件翻译成机器语言;根据各个平台不同的汇编语言来进行 “翻译”
    • 利用汇编器 as
    gcc -c main.s -o main.o
    
    • 1
    .5. 链接 -o
    • 合并段,进行符号解析,分配虚拟地址空间
      • 合并各个目标文件的.data段,.test段等等,重新调整偏移地址
      • 符号解析:找到符号表中的各个符号的唯一的定义
    • 进行符号的重定向
      • 一开始的目标文件是单独编译的,它的里面很多函数,方法并不知道存在的地址,在进行符号解析,为他们分配了虚拟地址空间之后,回过头来为这些符号填充上地址
    4. 链接其他库文件
    • 把链接库作为一般的目标文件,为 GCC 指定该链接库的完整路径与文件名。
    #链接库名为 libm.a,并且位于 /usr/lib 目录,那么下面的命令会让 GCC 编译 main.c,然后将 libm.a 链接到 main.o:
    gcc main.c -o main.out /usr/lib/libm.a
    
    • 1
    • 2
    • 使用 -L 选项,为 GCC 增加另一个搜索链接库的目录
    # 数学库的文件名是 libm.a。前缀 lib 和后缀.a 是标准的,m 是基本名称,GCC 会在 -l 选项后紧跟着的基本名称的基础上自动添加这些前缀、后缀,本例中,基本名称为 m。
    gcc main.c -o main.out -L/usr/lib -lm
    
    • 1
    • 2
    • 一次处理多个文件
    gcc myfun.c main.c -o main.exe
    
    gcc *.c -o main.exe   
    
    • 1
    • 2
    • 3
    5. 链接库
    .1. 静态链接库
    demo项目
       ├─ headers
       │     └─ test.h
       └─ sources
              ├─ add.c
              ├─ sub.c
              ├─ div.c
              └─ main.c
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    #直接编译
    gcc main.c add.c sub.c div.c -o main.exe
     
     #编译成静态链接库
     #  1. 将所有指定的源文件,都编译成相应的目标文件:
    gcc -c sub.c add.c div.c
    #   2. 然后使用 ar 压缩指令,将生成的目标文件打包成静态链接库,其基本格式如下:ar rcs 静态链接库名称 目标文件 1 目标文件 2 ...
    ar rcs libmymath.a add.o sub.o div.o
    
    #使用编译的静态链接库
     gcc -c main.c
     gcc -static main.o libmymath.a  # gcc main.o -static -L /root/demo/ -lmymath
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    .2. 动态链接库使用
    #1) 直接使用源文件创建动态链接库,gcc -fpic -shared 源文件名... -o 动态链接库名
    gcc -fpic -shared add.c sub.c div.c -o libmymath.so
    
    #2) 先使用 gcc -c 指令将指定源文件编译为目标文件。仍以 demo 项目中的 add.c、sub.c 和 div.c 为例,先执行如下命令:
    gcc -c -fpic add.c sub.c div.c
    gcc -shared add.o sub.o div.o -o libmymath.so
    
    #动态链接库使用
    gcc main.c  libmymath.so -o main.exe
    ldd main.exe    #查看当前文件在执行时需要用到的所有动态链接库,以及各个库文件的存储位置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 将链接库文件移动到标准库目录下(例如 /usr/lib、/usr/lib64、/lib、/lib64);
    • 在终端输入 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx,其中 xxx 为动态链接库文件的绝对存储路径(此方式仅在当前终端有效,关闭终端后无效);
    • 修改~/.bashrc 或~/.bash_profile 文件,即在文件最后一行添加 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx(xxx 为动态库文件的绝对存储路径)。保存之后,执行 source .bashrc 指令
    6. 库找不到问题
    1. 利用静态库或者动态库文件实现链接操作(生成可执行文件)时,GCC 可能会提示 “xxx:No such file or directory”(其中 xxx 表示查找失败的静态库或者动态库);
    2. 执行借助动态库生成的可执行文件时,GCC 可能会提示 “./main.exe: error while loading shared libraries: xxx.so: cannot open shared object file: No such file or directory”(其中 xxx 表示动态库的文件名)。
    .1. 链接操作无法找到库文件
    #GCC 编译器只会在当前目录中(这里为 demo 目录)查找 libmymath.a 静态链接库;
    gcc -static main.c libmymath.a -o main.exe  
    
    #使用 -l(小写的 L)选项指明了要查找的静态库的文件名,则 GCC 编译器会按照如下顺序,依次到指定目录中查找所需库文件:
    #  如果 gcc 指令使用 -L 选项指定了查找路径,则 GCC 编译器会优先选择去该路径下查找所需要的库文件;
    #  再到 Linux 系统中 LIBRARY_PATH 环境变量指定的路径中搜索需要的库文件;
    # 最后到 GCC 编译器默认的搜索路径(比如 /lib、/lib64、/usr/lib、/usr/lib64、/usr/local/lib、/usr/local/lib64 等,不同系统环境略有差异)中查找。
    gcc -static main.c -lmymath -o main.exe
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    .2. 执行操作无法找到库文件

    当 GCC 编译器运行可执行文件时,会按照如下的路径顺序搜索所需的动态库文件:

    1. 如果在生成可执行文件时,用户使用了 -Wl,-rpath=dir(其中 dir 表示要查找的具体路径,如果查找路径有多个,中间用:冒号分隔)选项指定动态库的搜索路径,则运行该文件时 GCC 会首先到指定的路径中查找所需的库文件;
    2. GCC 编译器会前往 LD_LIBRARY_PATH 环境变量指明的路径中查找所需的动态库文件;
    3. GCC 编译器会前往 /ect/ld.so.conf 文件中指定的搜索路径查找动态库文件; 该文件里面包含了若干自己编译的.so文件
    4. GCC 编译器会前往默认的搜索路径中(例如 /lib、/lib64、/usr/lib、/usr/lib64 等)中查找所需的动态库文件。

    注意,可执行文件的当前存储路径,并不在默认的搜索路径范围内,因此即便将动态库文件和可执行文件放在同一目录下,GCC 编译器也可能提示 “找不到动态库”。

    • 将动态库文件的存储路径,添加到 LD_LIBRARY_PATH 环境变量中。假设动态库文件存储在 /usr 目录中,通知执行 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr 指令,即可实现此目的(此方式仅在当前命令行窗口中有效);
    • 修改动态库文件的存储路径,即将其移动至 GCC 编译器默认的搜索路径中。
    • 修改~/.bashrc 或~/.bash_profile 文件,即在文件最后一行添加 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx(xxx 为动态库文件的绝对存储路径)。保存之后,执行 source .bashrc 指令(此方式仅对当前登陆用户有效)。
    7. GCC 编译制定头文件和库文件案例
      • g++ test.cpp -l l i b − L lib -L libLPATH -I i n c l u d e − W l , − r p a t h = include -Wl,-rpath= includeWl,rpath=rpath
      • -I跟头文件路径
      • -L:静态库目录
      • -l:静态库名字
      • Wl,-rpath=跟so依赖的so路径
      • 可以通过locate, find / -name “” 找到系统所在路径
      • 可以通过 rpm -qf 查找链接库所在的包,然后去下载
    8. Scons 构建项目
    • https://rebootcat.com/2020/08/30/scons/
    • https://chowdera.com/2022/04/202204090527222007.html
  • 相关阅读:
    十大排序算法(冒泡排序、插入排序、选择排序、希尔排序、堆排序、快排、归并排序、桶排序、计数排序、基数排序)
    面试之Java String 编码相关
    枚举、注解及单元测试的简单笔记
    软件设计模式
    [Servlet 4]Bean与DAO设计模式
    halcon知识:矩阵专题【02】
    第2周学习:卷积神经网络基础
    Json递归删除和修改节点
    登录中获取验证码的节流
    ssm+vue的软考系统(有报告)。Javaee项目,ssm vue前后端分离项目。
  • 原文地址:https://blog.csdn.net/liudongdong19/article/details/126685931