C++ 全栈知识体系C++ 全栈知识体系
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
关于
✿导航
  • 基础
  • 函数
  • 知识点
  • IO框架
  • 新版本特性
  • 数据库原理
  • SQL语言
  • SQL - MySQL
  • NoSQL - Redis
  • NoSQL - ElasticSearch
  • 算法基础
  • 常见算法
  • 领域算法
  • 分布式算法
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • 计算机组成
  • 开发
  • 测试
  • 架构基础
  • 分布式系统
  • 微服务
  • 中间件
  • 概念
  • 理论
  • 架构设计原则
  • 设计模式
  • 协议
  • 技术选型
  • 编码规范
  • 流水线构建 - CI/CD
  • 知识点 - Linux
  • 网站 - Nginx
  • 容器化 - Docker
  • 容器编排 - Kubernetes
  • 服务网格 - Service Mesh Istio
  • 常用快捷键 - Shortcut
  • 工具使用 - Tools
  • 开源项目
  • 学习项目
  • 个人项目
  • 项目开发
  • 项目Idea
  • 并发
  • 部署
  • 分布式
  • 知识
  • 问题
  • 编程语言与技术
  • 系统与架构
  • 软件开发实践
  • 数据处理与应用设计
  • 个人
  • 产品
  • 团队
  • 知识体系
  • Vue
关于
  • 流水线构建 - CI/CD

    • CI/CD - 基础
    • CI/CD - 部署
    • CI/CD - 本地仓库CI流程
    • CI/CD - 远程仓库CI流程
  • 知识点 - Linux

    • Linux - 常用命令大全
    • Linux - 信号
    • Linux - 线程
    • Linux - 进程
    • Linux - 线程和进程同步
    • Linux - shell命令使用技巧
    • Linux - 使echo命令输出结果带颜色
  • 网站 - Nginx

    • Nginx - 学习笔记
    • Nginx - 部署静态页面网站
    • Nginx - 反向代理服务器
    • Nginx - 错误日志配置及信息详解
    • Nginx - 图片服务器配置
  • 容器化 - Docker

    • Docker - 容器技术Docker、Compose、k8s的演变
    • Docker - Dockerfile学习
    • Docker - 命令大全
    • Docker - Docker-compose学习
  • 容器编排 - Kubernetes

    • Kubernetes - 概念
    • Kubernetes - 基础应用
    • Kubernetes - 命令
    • Kubernetes - 检测探针
  • 服务网格 - Service Mesh Istio

    • Istio - 基础
    • Istio - 安装
    • Istio - 使用
    • Istio - 架构解析
    • Istio - 可观察性
    • Istio - 通过阿里云ecs部署k8s集群
  • 常用快捷键 - Shortcut

    • Shortcut - Linux
    • Shortcut - Vim
    • Shortcut - VsCode
    • Shortcut - IDEA
  • 工具使用 - Tools

    • Tools - cmake使用指南
    • Tools - cmake install详解
    • Tools - curl使用方法
    • Tools - docker使用方法
    • Tools - GCC警告选项
    • Tools - GCC和动静态库
    • Tools - gdb调试方法
    • Tools - nginx安装以及使用
    • Tools - VsCode插件

Tools - GCC和动静态库

  • 语法
  • 选项
  • 示例
    • 阶段编译
  • 使用静态库
  • 使用共享库
    • 动态库修改使用
  • 动态库和静态库比较
    • 静态库
    • 动态库

参考

  • gcc 命令详解及最佳实践

概述

GCC(英文全拼:GNU Compiler Collection)是 GNU 工具链的主要组成部分,是一套以 GPL 和 LGPL 许可证发布的程序语言编译器自由软件,由 Richard Stallman 于 1985 年开始开发。

GCC 原名为 GNU C语言编译器,因为它原本只能处理 C 语言,但如今的 GCC 不仅可以编译 C、C++ 和 Objective-C,还可以通过不同的前端模块支持各种语言,包括 Java、Fortran、Ada、Pascal、Go 和 D 语言等等。

编译过程

GCC 的编译过程可以划分为四个阶段:预处理(Pre-Processing)、编译(Compiling)、汇编(Assembling)以及链接(Linking)。

Linux 程序员可以根据自己的需要控制 GCC 的编译阶段,以便检查或使用编译器在该阶段的输出信息,帮助调试和优化程序。以 C 语言为例,从源文件的编译到可执行文件的运行,整个过程大致如下。

语法

gcc [options] file...

选项

可以通过 gcc --help 查看具体的选项内容。

[root@VM-16-6-centos test]# gcc --help
Usage: gcc [options] file...
Options:
  -pass-exit-codes         Exit with highest error code from a phase
  --help                   Display this information
  --target-help            Display target specific command line options
  --help={common|optimizers|params|target|warnings|[^]{joined|separate|undocumented}}[,...]
                           Display specific types of command line options
  (Use '-v --help' to display command line options of sub-processes)
  --version                Display compiler version information
  -dumpspecs               Display all of the built in spec strings
  -dumpversion             Display the version of the compiler
  -dumpmachine             Display the compiler's target processor
  -print-search-dirs       Display the directories in the compiler's search path
  -print-libgcc-file-name  Display the name of the compiler's companion library
  -print-file-name=<lib>   Display the full path to library <lib>
  -print-prog-name=<prog>  Display the full path to compiler component <prog>
  -print-multiarch         Display the target's normalized GNU triplet, used as
                           a component in the library path
  -print-multi-directory   Display the root directory for versions of libgcc
  -print-multi-lib         Display the mapping between command line options and
                           multiple library search directories
  -print-multi-os-directory Display the relative path to OS libraries
  -print-sysroot           Display the target libraries directory
  -print-sysroot-headers-suffix Display the sysroot suffix used to find headers
  -Wa,<options>            Pass comma-separated <options> on to the assembler
  -Wp,<options>            Pass comma-separated <options> on to the preprocessor
  -Wl,<options>            Pass comma-separated <options> on to the linker
  -Xassembler <arg>        Pass <arg> on to the assembler
  -Xpreprocessor <arg>     Pass <arg> on to the preprocessor
  -Xlinker <arg>           Pass <arg> on to the linker
  -save-temps              Do not delete intermediate files
  -save-temps=<arg>        Do not delete intermediate files
  -no-canonical-prefixes   Do not canonicalize paths when building relative
                           prefixes to other gcc components
  -pipe                    Use pipes rather than intermediate files
  -time                    Time the execution of each subprocess
  -specs=<file>            Override built-in specs with the contents of <file>
  -std=<standard>          Assume that the input sources are for <standard>
  --sysroot=<directory>    Use <directory> as the root directory for headers
                           and libraries
  -B <directory>           Add <directory> to the compiler's search paths
  -v                       Display the programs invoked by the compiler
  -###                     Like -v but options quoted and commands not executed
  -E                       Preprocess only; do not compile, assemble or link
  -S                       Compile only; do not assemble or link
  -c                       Compile and assemble, but do not link
  -o <file>                Place the output into <file>
  -pie                     Create a position independent executable
  -shared                  Create a shared library
  -x <language>            Specify the language of the following input files
                           Permissible languages include: c c++ assembler none
                           'none' means revert to the default behavior of
                           guessing the language based on the file's extension

Options starting with -g, -f, -m, -O, -W, or --param are automatically passed on to the various sub-processes invoked by gcc.  In order to passother options on to these processes the -W<letter> options must be used.

示例

阶段编译

假设有文件 hello.c,内容如下:

#include <stdio.h>

int main(void)
{
    printf("Hello, World!\n");
    return 0;
}

编译 hello.c,默认输出 a.out

gcc hello.c

编译 hello.c 并指定输出文件为 hello

gcc hello.c -o hello

只执行预处理,输出 hello.i 源文件

gcc -E hello.c -o hello.i

只执行预处理和编译,输出 hello.s 汇编文件

gcc -S hello.c

也可以由 hello.i 文件生成 hello.s 汇编文件

gcc -S hello.i -o hello.s

只执行预处理、编译和汇编,输出 hello.o 目标文件

gcc -c hello.c

也可以由 hello.i 或 hello.s 生成目标文件 hello.o

gcc -c hello.i -o hello.o
gcc -c hello.s -o hello.o

由 hello.o 目标文件链接成可执行文件 hello

gcc hello.o -o hello

hello.i 和 hello.s 文件内容如下

详情

hello.i: ./src/gcc/hello.i

hello.s: ./src/gcc/hello.s

使用静态库

创建一个 fun.c 文件,内容如下:

详情

#include <stdio.h>

void fun(void) { printf("Here is a static library!\n"); }

将 fun.c 编译成静态库 libfun.a

gcc -c fun.c
ar rcs libfun.a fun.o

查看文件描述 TODO

$ file *
fun.c:    C source, ASCII text
fun.o:    ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
libfun.a: current ar archive

修改 hello1.c 文件,调用 fun 函数

详情

#include <stdio.h>

void fun(void);

int main(void) { printf("Hello, World!\n"); fun(); return 0; }

编译 hello1.c 并链接静态库 libfun.a(加上 -static 选项)

gcc hello1.c -static libfun.a -o hello1

也可以使用 -L 指定库的搜索路径,并使用 -l 指定库名

gcc hello1.c -L. -static -lfun -o hello1

运行结果

[root@VM-16-6-centos test]# ./hello1
Hello, World!
Here is a static library!

查看 hello1 文件描述

[root@VM-16-6-centos test]# file hello1
hello1: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=0fc4821ab804afd1d4a1d486616964714e9a7f6b, not stripped

使用共享库

创建一个 shareFun.c 文件,内容如下:

详情

#include <stdio.h>

void fun(void) { printf("Here is a shared library!\n"); }

编译 hello1.c 并链接共享库 libshareFun.so

gcc shareFun.c -shared -fPIC -o libshareFun.so

也可以使用 -L 和 -l 选项指定库的路径和名称

gcc hello1.c -L. -lshareFun -o hello1

运行结果

[root@VM-16-6-centos test]# export LD_LIBRARY_PATH=$(pwd)
[root@VM-16-6-centos test]# ./hello1
Hello, World!
Here is a shared library!

动态库修改使用

# 修改动态库并重新生成
[root@VM-16-6-centos test]# vi shareFun.c
[root@VM-16-6-centos test]# gcc shareFun.c -shared -fPIC -o libshareFun.so

# 不需要重新编译 hello1,新增内容 “Test”,可以直接看到,运行时重新加载so
[root@VM-16-6-centos test]# ./hello1
Hello, World!
Test Here is a shared library!

查看链接内容:

[root@VM-16-6-centos test]# ldd hello1
        linux-vdso.so.1 =>  (0x00007fff03bbd000)
        /$LIB/libonion.so => /lib64/libonion.so (0x00007fd57b8b1000)
        libshareFun.so => /home/klc/test/libshareFun.so (0x00007fd57b596000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fd57b1c8000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007fd57afc4000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fd57b798000)

修改完后,新增内容 “111”,查看链接信息的确变化了。

[root@VM-16-6-centos test]# vi shareFun.c
[root@VM-16-6-centos test]# gcc shareFun.c -shared -fPIC -o libshareFun.so
[root@VM-16-6-centos test]# ./hello1
Hello, World!
Test111 Here is a shared library!
[root@VM-16-6-centos test]# ldd hello1
        linux-vdso.so.1 =>  (0x00007ffe2e427000)
        /$LIB/libonion.so => /lib64/libonion.so (0x00007f11fdad2000)
        libshareFun.so => /home/klc/test/libshareFun.so (0x00007f11fd7b7000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f11fd3e9000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f11fd1e5000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f11fd9b9000)

动态库和静态库比较

静态库

静态库是程序在编译链接时把库的代码复制到可执行文件中,生成的可执行程序在运行时将不再需要静态库,因此使用静态库生成的可执行程序的大小一般比较大。

  • 优点
    • 使用静态库生成可执行程序后,该可执行程序就可以独自运行,不再需要库了。
  • 缺点
    • 使用静态库生成可执行程序会占用大量空间,特别是当有多个静态程序同时加载而这些静态程序使用的都是相同的库,这时在内存当中就会存在大量的重复代码。

动态库

动态库是程序在运行时才去链接相应的动态库代码的,多个程序共享使用库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。

在可执行文件开始运行前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接。动态库在多个程序间共享,节省了磁盘空间,操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

  • 优点
    • 节省磁盘空间,且多个用到相同动态库的程序同时运行时,库文件会通过进程地址空间进行共享,内存当中不会存在重复代码。
    • 加载器在加载动态库时,操作系统会先检查动态库是否因为其它程序已经将这个动态库信息加载到了内存中。如果没有加载到内存中,操作系统会将动态库载入内存,并将它的引用计数设置为1;如果已经加载到内存,仅将动态库的引用计数加1。
  • 缺点
    • 必须依赖动态库,否则无法运行。
Last Updated:
Contributors: klc407073648
Prev
Tools - GCC警告选项
Next
Tools - gdb调试方法