title: Linux - g++ 命令
date: 2023-08-07 00:00:00
categories: [Linux]
tags: [Linux, g++]
Linux - g++ 命令
g++ 是 GNU 开发的 C++ 编译器,gcc 是 GNU 的 C 编译器,都是 GNU 编译器套件 GCC (GNU Compiler Collection) 的组成部分。
这里将介绍常用的命令。
1. 编译简介
g++ 编译分四个步骤:预处理 (Preprocessing)、编译 (Compilation)、汇编 (Assembly) 和链接 (Linking)。
g++ 命令包装了预处理器、编译器、汇编器和链接器,会根据不同的参数去调用这些构建程序。
以 C++ 源码文件 test.cpp 为例:
#include <iostream>
int main(void)
{
std::cout << "Hello world!" << std::endl;
return 0;
}
下面将 test.cpp 转换为可执行文件 test.out。
-
预处理 (Preprocessing)
预处理器 cpp 将 .cpp 源码预处理为 .i 文件。预处理将处理预处理指令(如 #include、#define、#if 等)和注释,不处理编译指令 #pragma。
# 生成预处理后的 .i 文件 g++ -E test.cpp -o test.i # 等效命令 cpp test.cpp -o test.i -
编译 (Compilation)
编译器 cc1plus 通过一系列语法分析、词法分析、语义分析及优化将 .i 文件编译为 .s 汇编代码文件。使用 -S 选项,只进行编译而不汇编,输出汇编代码。
# 生成汇编 .s 文件 g++ -S test.i [-o test.s] # 等效命令 # 第一行是创建 cc1plus 软连接,第二行是使用 cc1plus 命令 ln -s /usr/libexec/gcc/x86_64-linux-gnu/7.3.0/cc1plus /usr/local/bin cc1plus test.i [-o test.s]补充:GCC 编译套件中,C++ 的编译器是 cc1plus,C 是 cc1,Objective-C 是 cc1obj,Fortran 是 f771,Java 是 jc1。
-
汇编 (Assembly)
汇编器 as 将 .s 文件汇编为 .o 可重定位目标文件,包含二进制代码和数据。
# 生成二进制 .o 文件 g++ -c test.s [-o test.o] # 等效命令 as test.s -o test.o -
链接 (Linking)
链接器 ld 将 .o 文件链接为可执行目标文件,包含了完整的二进制代码和数据。
# 生成二进制 .out 可执行文件 g++ test.o -o test.out # 等效命令 # 直接使用 ld 进行链接,但是需要注意使用 ld 需添加较长命令选项 # 可以使用 g++ -v <test.o> 命令查看 collect2 所使用的命令选项 # collect2 是对 ld 的封装,最终还是要调用 ld 来完成链接工作 ld <collect2 命令选项> -o test.out
执行最后链接生成的 test.out 输出:
Hello world!
以上过程合成一步:
g++ test.cpp -o test.out
2. 命令格式
gcc [-c|-S|-E] [-std=standard]
[-g] [-pg] [-Olevel]
[-Wwarn...] [-Wpedantic]
[-Idir...] [-Ldir...]
[-Dmacro[=defn]...] [-Umacro]
[-foption...] [-mmachine-option...]
[-o outfile] [@file] infile...
g++ 接受与 gcc 基本相同的选项。
3. 命令选项
关于 g++ 命令选项,可以使用 man g++ 查看,或参考 GCC 官方手册:gcc.pdf (gnu.org)。
下面介绍常用的命令选项。
3.1. 总体选项
-E
只激活预处理,但不生成文件。
可以使用重定向、管道或 -o 选项输出到文件中或查看。例如:
g++ -E test.cpp > test.i
g++ -E test.cpp | more
g++ -E test.cpp -o test.i
-S
只激活预处理和编译,将文件编译为 .s 汇编代码文件。例如:
g++ -S test.cpp
-c
只激活预处理,编译和汇编,将文件汇编为 .o 可重定位目标文件。例如:
g++ -c test.cpp
-o
指定目标名称。
输出可执行目标文件时,缺省为 a.out。例如:
g++ test.cpp -o test.out
g++ -S test.cpp -o test.s
3.2. 目录选项
-iquote [dir]
-I[dir]
-isystem [dir]
-idirafter [dir]
这四个命令选项都是将 dir 添加到头文件搜索路径列表。
多个时,在各个种类内,按从左到右的优先级顺序处理。
-iquote [dir] 仅对 #include "file" 有效。
-I[dir], -isystem [dir], -idirafter [dir] 对 #include "file" 和 #include <file> 有效。
因此,搜索顺序如下:
#include "file" 头文件搜索顺序:
当前目录 -> -iquote [dir] -> -I[dir] -> -isystem [dir] -> 系统目录 -> -idirafter [dir]
#include <file> 头文件搜索顺序:
-I[dir] -> -isystem [dir] -> 系统目录 -> -idirafter [dir]
-iprefix [prefix]
-iwithprefix [dir]
一起使用,会到 prefix + dir 下查找,搜索顺序相当于 -idirafter [dir]。
如果 prefix 为目录,应以 / 结尾。
-I-
已弃用,现使用 -iquote [dir]。
-I- 前(左)的 -I[dir] 仅仅对 #include "file" 有效,
-I- 后(右)的 -I[dir] 同时对 #include "file" 和 #include <file> 有效。
-include [file]
相当于将 #include "file" 添加到代码第一行,多个时最左边的优先级最高。
默认路径为预处理器所在目录,建议使用绝对路径。
-L[dir]
将 dir 添加到要搜索 -l 的路径列表中,即添加搜索库的路径。
-l[library]
链接时将搜索名为 library 库,默认搜索系统目录。链接顺序从左到右。
同时找到动态库和静态库时,链接器会优先链接动态库,除非使用了 -static 选项。
推荐用于动态库,如 libtest.so,参数写作 -ltest。
静态库直接写上绝对路径即可。
3.3. 预处理选项
-D[macro]
相当于 #define macro。
-D[macro=defn]
相当于 #define macro defn。
若要定义字符串,为 -Dmacro=\"defn\"
-U[macro]
相当于 #undef macro。
-undef
取消任何非标准宏的定义,C++ 标准预定义的宏仍然有效。
3.4. 链接方式选项
-static
禁止使用动态库。
-shared
尽量使用动态库,为默认选项。
-symbolic
建立共享目标文件的时候,把引用绑定到全局符号上。
警告所有无法解析的引用(除非被链接选项 -Xlinker -z -Xlinker defs 覆盖)。
只有少数系统支持该选项。
-Wl,[option]
使用链接器 ld 参数 option。
如果 option 中间有逗号,将分成多个选项传递给链接器。
-Wl,-Bstatic
禁止使用动态库,如果只存在动态链接库,则链接器报错。
-Wl,-Bdynamic
优先使用动态库,如果只存在静态链接库,则使用静态链接库。
-Wl,-rpath,[dir]
设置运行时共享库的搜索路径为 dir。
-Wl,-s 或 -Wl,--strip-all
剔除所有符号信息。
-Wl,-S 或 -Wl,--strip-debug
剔除调试符号信息。
3.5. 错误与警告选项
-pedantic
允许发出 ANSI/ISO C 标准所列出的所有警告。
-pedantic-errors
允许发出 ANSI/ISO C 标准所列出的错误。例如:
使用 -std=c++11 使用 c++11 标准后,若使用 c++14 语法,
在 g++ 版本支持的情况下,仅会发出警告,仍能完成编译。
但若启用 -pedantic-errors, 将警告转为错误,则需严格遵守标准才能编译。
-Wall
一般使用该选项,允许发出 GCC 能够提供的所有有用的警告。
也可以用 -W[warning] 来标记指定的警告。
-Wno-deprecated
使用 C++ 标准废弃特性不告警。
-Werror
将所有警告转为错误。
-Werror=[warning]
将指定警告转为错误。例如:
-Werror=return-type,如果函数需要返回值却没有 return 语句,则编译报错。
-Wunknown-pragmas
遇到 GCC 无法识别的编译指导指令时,发出警告。已由 -Wall 选项包含。
-Wno-pragmas
遇到 GCC 无法识别的编译指导指令时,不发出警告。
-w
关闭所有警告,不建议使用。
3.6. 调试选项
-g
可执行目标文件包含调试信息。
-gstabs
此选项以 stabs 格式生成调试信息,但不包括 gdb 调试信息。
-gstabs+
此选项以 stabs 格式声称调试信息,并且包含仅供 gdb 使用的额外调试信息。
-ggdb
此选项将尽可能的生成 gdb 可以使用的调试信息。
-g<level>
请求生成调试信息,同时用 level 指出需要多少信息,默认的 level 是 2。
0 不产生调试信息,1 信息最少,3 包含宏信息。
-pg
编译的过程中加入额外的代码,供性能分析工具 gprof 剖析程序的耗时情况。
3.7. 优化选项
-O0
-O1
-O2
-O3
-Os
优化级别,在编译时间、程序大小和执行效率上有所区别。
-O0,减少编译时间,并且不影响调试信息。是默认的编译选项。
-O1 或 -O,优化。
-O2,进一步优化。
-O3,更进一步优化。
-Os,优化大小,启用除了通常会增加代码大小的优化选项以外的所有 -O2 的优化选项。
3.8. 其他选项
-fpic
编译器生成位置无关目标码(PIC, position-independent code),用于动态链接库。
通过全局偏移表(GOT, Global Offset Table)访问所有常量地址。
程序启动时通过动态加载程序解析 GOT 条目。
如果动态链接库的 GOT 大小超过限制,链接器会报错,
提示 -fpic 不起作用,请使用 -fPIC。
-fPIC
同 -fpic 功能一致,生成位置无关目标码,用于生成动态链接库。
建议使用该选项,而非 -fpic。
-v
--verbose
显示详细的编译、汇编、链接命令。
-pipe
使用管道代替编译过程中的临时文件。
在使用非 gnu 汇编工具的时候,可能有些问题。例如:
g++ -pipe -o hello.out hello.cpp
-std=[standard]
明确语言标准,对 C 或 C++ 有效。
比如 -std=c++11 将指定 2011 年 ISO C++ 标准及其修订版。
-ansi
对 C 等效于 -std=c90,对 C++ 等效于 -std=C++98。
关闭 gnu c 中与 ansi c 不兼容的特性,激活 ansi c 的专有特性。
包括禁止一些 asm, inline, typeof 关键字,以及 UNIX, vax 等预处理宏。
-fno-asm
此选项实现 ansi 选项功能的一部分,
它禁止将 asm, inline 和 typeof 用作关键字。
-fno-strict-prototype
只对 g++ 起作用,使用这个选项,
g++ 将把函数的不带参数当作是没有显式的对参数的个数和类型说明,而不是没有参数。
而 gcc 无论是否使用这个参数,都将对没有带参数的函数,认为没有显式说明的类型。
-fthis-is-varialble
向传统 c++ 看齐,可以将 this 当作一般变量使用。
-fcond-mismatch
允许条件表达式的第二和第三参数类型不匹配,表达式的值将为 void 类型。
-funsigned-char
-fno-unsigned-char
-fsigned-char
-fno-signed-char
设置 char 的类型。
-fpermissive
把代码的语法错误作为警告,并继续编译。请谨慎使用该选项。
-imacros [file]
将 file 文件的宏,扩展到 gcc/g++ 的输入文件。
宏定义本身并不出现在输入文件中。
-nostdinc
使编译器不在系统缺省的头文件目录里面找头文件。
一般和 -I 联合使用,明确限定头文件的位置。
-nostdin C++
规定不在 g++ 指定的标准路经中搜索,但仍在其他路径中搜索。
此选项在创建 libg++ 库时使用。
-C
在预处理的时候,不删除注释信息。
一般和 -E 使用,方便分析程序。
-m
生成与具体 CPU 相关的程序。
-mtune=[cpu-type]
为指定类型的 CPU 生成代码。
cpu-type 可以是:i386,i486,i586,pentium,i686,pentium4 等等。
-m32
生成 32 位程序。int,long 和指针位宽为 32 位。
-m64
生成 64 位程序。int 位宽为 32 位,long 和指针位宽为 64 位。
-mmmx
-msse
-msse2
-mno-mmx
-mno-sse
-mno-sse2
使用或者不使用 MMX,SSE,SSE2 指令。
-M
生成文件依赖的信息,包含目标文件所依赖的所有源文件。例如:
g++ test.cpp -M
-MM
与 -M 相似,但是它将忽略由 #include <file> 造成的依赖关系。
-MD
和 -M 相同,但是将结果输出到 .d 文件里。
-MMD
和 -MM 相同,但是将结果输出到 .d 文件里。
-Wa,[option]
此选项传递 option 给汇编器。
如果 option 中间有逗号,就将 option 分成多个选项传递给汇编器。
-x [language] [filename]
设定文件使用的语言,使后缀名无效。
对以后多个文件有效,直到其他选项或参数覆盖。
可选参数比如:
c c-header cpp-output
c++ c++-header c++-cpp-output
assembler assembler-with-cpp, 等等。例如:
gcc -x c hello.pig
-x none [filename]
关掉上一个选项,让 gcc 根据文件名后缀,自动识别文件类型。例如:
gcc -x c hello.pig -x none hello2.c
4. 链接注意事项
4.1. 混合链接
g++ 链接库时,若静态库与动态库相同,默认优先链接动态链接库。
静态库与动态库混合链接时,若要指定静态动态,有如下两种方法:
-
静态链接库使用绝对路径,动态链接库使用
-l。例如:g++ test.cpp -o test.out /usr/lib64/libstatic.a -L/usr/lib64 -ldynamic -
都使用
-l,但在静态链接库前加-Wl,-Bstatic,动态链接库前加-Wl,-Bdynamic。例如:g++ test.cpp -o test.out -L/usr/lib64 -Wl,-Bstatic -lstatic -Wl,-Bdynamic -ldynamic最后需以
-Wl,-Bdynamic结束,作用是让后续的系统库默认使用动态链接。
4.2. 选项顺序
链接时,库要放在目标文件的后面,否则会报 "undefined reference to: xxx" 错误。
具体参见 gcc 手册的如下描述:
It makes a difference where in the command you write this option;
the linker searches and processes libraries and object files in the
order they are specified. Thus, foo.o -lz bar.o searches library z
after file foo.o but before bar.o. If bar.o refers to functions in
z, those functions may not be loaded.