当前位置: 首页>后端>正文

Linux 基础教程 - 使用 GCC 编译器编译二进制

GCC 编译器介绍

GCC 是一种免费的开源编译器,适用于多种编程语言,包括 C、C++、Objective-C、Fortran、Ada 等。开源优势意味任何人都可以根据自己的需要自由修改和分发 GCC 的源代码,除开源以外,GCC 还有以下几个优势:

  • 跨平台兼容性
    GCC 可以在各种操作系统上运行,包括 Linux、macOS 和 Windows。 这可以大幅度提高不同系统上的工作协同效率。

  • 模块化架构
    GCC 被设计为一个模块化系统,不同的组件可以根据用户的要求进行组合或替换。 这种模块化允许 GCC 支持多种语言和体系结构,还允许用户根据自己的需要添加或删除功能。

  • 优化代码生成效率
    GCC 通过了循环展开、指令调度和分支预测等多种技术实现代码生成优化,使其生成运行速度更快且使用更少系统资源的优化代码。

  • 遵从行业标准:
    GCC 符合各种行业标准,例如 C99 和 C++11 标准,使其成为 C 和 C++ 开发的可靠且广泛接受的选择。 GCC 还支持对这些标准的各种扩展,允许开发人员编写特定于他们要求的代码。

  • 社区支持
    GCC 拥有庞大而活跃的开发人员和用户社区,他们为其开发和维护做出贡献。 该社区为用户提供支持和资源,使开发人员更容易使用 GCC 并在需要时获得帮助。

  1. Linux 开发C/C++ 一定要熟悉 GCC
  2. VSCode 是通过调用 GCC 编译器来实现 C/C++ 的编译工作的;
    实际使用中:
    使用 gcc 指令编译 C 代码
    使用 g++指令编译 C++ 代码

编译过程简析

  1. 预处理 - Pre-Processing // i文件
    -E 选项指示编译器仅对输入文件进行预处理
g++ -E main.cpp -o main.i // i文件

2。 编译 - Compiling // s 文件
-S 编译选项告诉 g++ 在为 C++ 代码产生了汇编语言文件后停止编译,g++ 产生的汇编语言文件的缺省扩展名是 .s

g++ -S main.i -o main.s

3。 汇编 - Assembling // .o文件
-c 选项告诉 g++ 仅把源代码编译为机器语言的目标代码,缺省时 g++ 建立的目标代码文件有一个 .o 的扩展名

g++ -c main.s -o main.o

4。 链接 - Linking // bin 文件
-o 编译选项来为将产生的可执行文件用指定的文件名

g++ main.o -o main

5。 以上可以综合为

g++ main.cpp -o main

g++ 重要编译参数

-g 编译带调试信息的可执行文件

-g 选项告诉 GCC 产生能被 GNU 调试器 GDB 使用的调试信息,以调试程序。
产生带调试信息的可执行文件 main

g++ -g main.cpp

-O[n] 优化源代码

所谓优化,例如省略掉代码中从未使用过的变量、直接将常量表达式用结果值代替等等,这些操作会使编译的速度降低,同时缩减目标文件所包含的代码量,提高最终生成的可执行文件的运行效率。

  • -O 选项告诉 g++ 对源代码进行基本优化。 这些优化在大多数情况下都会使程序执行的更快,同时减小代码的长度和执行时间,其效果等价于-O1
  • -O0 表示不做优化
  • -O1 为默认优化
  • -O2 除了完成 -O1 的优化之外,还进行一些额外的调整工作,如指令调整等。
  • -O3 则包括循环展开和其他一些与处理特性相关的优化工作。
    使用 -O2优化源代码,并输出可执行文件
g++ -O2 main.cpp

-l 和 -L 指定库文件 | 指定库文件路径

-l 参数(小写)就是用来指定程序要链接的库,-l 参数紧接着就是库名在 /lib/usr/lib/usr/local/lib 里的库直接用 -l 参数就能链接,比如链接 glog 库。

g++ -lglog main.cpp

如果库文件没放在上面三个目录里,需要使用 -L 参数(大写)指定库文件所在目录,-L 参数跟着的是库文件所在的目录名,链接 mytest 库,/home/bing/mytestlibfolder/libmytest.so。

g++ -L/home/bing/mytestlibfolder -lmytest main.cpp

-I 指定头文件搜索目录

/usr/include 目录一般是不用指定的,gcc 知道去那里找,但是如果头文件不在 /usr/icnclude
里我们就要用 -I 参数指定了,比如头文件放在 /myinclude 目录里,那编译命令行就要加上 -I/myinclude 参数了,如果不加你会得到一个"xxxx.h: No such file or directory" 的错误。 -I 参数可以用相对路径,比如头文件在当前目录,可以用-I。来指定。 上面我们提到的 –cflags 参数就是用来生成 -I 参数的。
g++ -I/myinclude main.cpp

-Wall 打印警告信息

打印出 gcc 提供的警告信息

g++ -Wall main.cpp

-w 关闭警告信息

g++ -w main.cpp

-std=c++11 设置编译标准

使用 c++11 标准编译 main.cpp

g++ -std=c++11 main.cpp

-o 指定输出文件名

指定即将产生的文件名

g++ main.cpp -o niam

-D 定义宏

在使用 gcc/g++ 编译的时候定义宏
常用场景: -DDEBUG 定义 DEBUG 宏,可能文件中有 DEBUG 宏部分的相关信息,用个 DDEBUG来 选择开启或关闭

#include <iostream>

int main()
{
#ifdef DEBUG
    std::cout << "DEBUG" << std::endl;
#endif
    // system("pause");
    return 0;
}
g++ -DDEBUG main.cpp -o niam

注: 使用 man gcc 命令可以查看gcc英文使用手册

用例

最初目录结构

.
├── include
│   └── StaticPolymorphism.h
├── main.cpp
└── src
   └── StaticPolymorphism.cpp

2 directories,3 files

直接编译

  • 最简单的编译,并运行
# 将 main.cpp src/StaticPolymorphism.cpp 编译为可执行文件
g++ -Iinc main.cpp src/StaticPolymorphism.cpp
# 运行 a.out
time ./a.out
  • 增加参数编译,并运行
# 将 main.cpp src/StaticPolymorphism.cpp 编译为可执行文件 附带一堆参数
g++ -g -std=c++11 -O2 -Wall -Iinc main.cpp src/StaticPolymorphism.cpp -o main
# 运行 main
time ./main

链接静态库生成可执行文件

# 进入src目录下
cd src

# 汇编,生成 StaticPolymorphism.o 文件
g++ StaticPolymorphism.cpp -c -I../inc

# 生成静态库 libStaticPolymorphism.a
ar rs libStaticPolymorphism.a StaticPolymorphism.o

# 回到上级目录
cd ..

# 链接,生成可执行文件: main-with-static-link
g++ main.cpp -Iinc -Lsrc -lStaticPolymorphism -o main-with-static-link

详细流程

  • 使用 g++ -c 命令 cpp 源文件编译成可重定位目标文件(.o文件)
g++ StaticPolymorphism.cpp -c -I../inc
  • 使用 ar rs 命令将可重定位目标文件打包成静态库。
    库文件名都是以 lib 开头的,静态库以 .a 作为后缀,表示 Archive。 ar 命令将目标文件打包成静态库,选项 r 表示将后面的文件列表添加到文件包,如果文件包不存在就创建它,如果文件包中已有同名文件就替换成新的。 选项 s 是专用于生成静态库的,表示为静态库创建索引,这个索引被链接器使用。
ar rs libStaticPolymorphism.a StaticPolymorphism.o
  • 把静态库和 main.cpp 编译链接在一起。 例如
g++ main.cpp -Iinc -Lsrc -lStaticPolymorphism -o main-with-static-link

其中,-L 选项告诉编译器去哪里找需要的库文件,-lStaticPolymorphism 告诉编译器要链接 libStaticPolymorphism 库, -I 选项告诉编译器去哪里找头文件。 注意,即使库文件就在当前目录,编译器默认也不会去找的,所以 -L。 选项不能少。 编译器默认的查找目录可以用-print-search-dirs 选项查看。

编译器会在默认搜索路径以及 -L 选项指定的路径中查找用 -l 选项指定的库,比如 -lStaticPolymorphism,编译器会首先找有没有动态库 libStaticPolymorphism.so,如果有就链接它,如果没有就找有没有静态库 libStaticPolymorphism.a,如果有就链接它。 所以编译器是优先考虑动态库的,如果希望编译器本次编译只链接静态库,可以指定 -static 选项。

在链接静态库时,链接器会把静态库中的目标文件取出来和可执行文件真正链接在一起。 链接器可以从静态库中只取出需要的目标文件来做链接。 而且使用静态库只需写一个库文件名,而不需要写一长串目标文件名。

最终目录结构

.
├── inc
│   └── StaticPolymorphism.h
├── main.cpp
├── main-with-static-link
├── README.md
└── src
    ├── libStaticPolymorphism.a
    ├── StaticPolymorphism.cpp
    └── StaticPolymorphism.o

运行可执行文件

./main-with-static-link

链接动态库生成可执行文件

# 进入src目录下
cd src

# 生成动态库 libStaticPolymorphism.so
g++ StaticPolymorphism.cpp -I../inc -fPIC -shared -o libStaticPolymorphism.so

# 上面命令等价于以下两条命令
g++ StaticPolymorphism.cpp -I../inc -c -fPIC
g++ -shared -o libStaticPolymorphism.so StaticPolymorphism.o

# 回到上级目录
cd ..

# 链接,生成可执行文件: main-with-dynamic-link
g++ main.cpp -Iinc -Lsrc -lStaticPolymorphism -o main-with-dynamic-link

详细流程

  • 使用 g++ -c -fPIC 命令 将 cpp 源文件编译成可重定位目标文件。 组成动态库的目标文件和一般的目标文件有所不同,在编译时要加 -fPIC 选项
g++ StaticPolymorphism.cpp -I../inc -c -fPIC
  • 使用 g++ -shared -o 命令将目标文件编译成动态库。
g++ -shared -o libStaticPolymorphism.so StaticPolymorphism.o
  • 把 main.c 和动态库编译链接在一起,例如:
g++ main.cpp -Iinc -Lsrc -lStaticPolymorphism -o main-with-dynamic-link

使用动态库编译链接生成的可执行文件只是包含动态库信息,并没有真的做动态链接。 在可执行文件加载到内存时,根据程序包含的动态库信息做动态链接。
运行可执行文件main可以会报错:

./main-with-dynamic-link: error while loading shared libraries: libStaticPolymorphism.so: cannot open shared object file: No such file or directory

编译的时候没问题,由于指定了 -Lsrc 选项,编译器可以在 src 目录下找到 libstack.so,而运行时却说找不到 libstack.so。 可以使用 ldd 命令查看可执行文件依赖于哪些动态库。

ldd main-with-dynamic-link
linux-vdso.so.1 (0x00007fffe9fbb000)
libStaticPolymorphism.so => not found
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f51da000000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f51d9c00000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f51da263000)
/lib64/ld-linux-x86-64.so。2 (0x00007f51da362000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f51da243000)

解决方法:

  1. 把 libStaticPolymorphism.so 所在目录的绝对路径 (比如/home/klein/compiler-case/compilation-dynamic-link/src) 添加到 /etc/ld.so.conf 中(该文件中每个路径占一行); 然后运行ldconfig命令。 ldconfig 命令会处理 /etc/ld.so.conf 中配置的目录和一些系统默认目录,如/lib,/usr/lib等,处理之后生成 /etc/ld.so.cache 缓存文件,动态链接器就从这个缓存中搜索动态库。
  2. 把 libStaticPolymorphism.so 拷到 /usr/lib 或 /lib 目录,这样可以确保动态链接器能找到这个动态库。
  3. 使用 LD_LIBRARY_PATH 环境变量预先加载 libStaticPolymorphism.so 所在目录。

最终目录结构

.
├── inc
│   └── StaticPolymorphism.h
├── main.cpp
├── main-with-dynamic-link
├── README.md
└── src
    ├── libStaticPolymorphism.so
    ├── StaticPolymorphism.cpp
    └── StaticPolymorphism.o

2 directories,7 files

运行可执行文件

LD_LIBRARY_PATH=src ./main-with-dynamic-link

https://www.xamrdz.com/backend/3h91931773.html

相关文章: