GCC¶
GCC全称是GNU Compiler Collection,是GNU编译集。
CNU全称是GNU's Not Unix,GNU 是一个类 Unix 操作系统,包括Linux内核和其它GNU软件包和由第三方发布的自由软件。
1 gcc 介绍¶
GCC和gcc是不同的东西,gcc是指GNU c 编译器。g++是指c++编译器。
- 功能:gcc一般用来编译c文件,但也可以用来编译其它文件,默认以文件结尾来判断:
- xxx.c:默认以编译 C 语言程序的方式编译此文件;
- xxx.cpp:默认以编译 C++ 程序的方式编译此文件。
- xxx.m:默认以编译 Objective-C 程序的方式编译此文件;
- xxx.go:默认以编译 Go 语言程序的方式编译此文件;
- gcc也能通过
-x
指定编译语言,如gcc -xc++ -lstdc++ 1.cpp
建议:虽然gcc可以编译其它语言,但还是以gcc编译c文件,以g++编译c++文件。因为gcc要编译c++文件需要设置多个编译选项,这样不仅麻烦还可能出错,如下所示
//1.cpp
#include<iostream>
using namespace std;
int main(){
cout<<"hello world!"<<endl;
}
- 使用gcc编译:
gcc -xc++ -lstdc++ 1.cpp
- 使用g++编译:
g++ 1.cpp
2 源文件命令规范¶
- c 源文件后缀为:
.c
- c 头文件后缀为:
.h
- c++源文件后缀包括:
.cc .cp .cxx .cpp .CPP .c++ .C
- c++头文件后缀包括:
.h .hh .H .hp .hxx .hpp .HPP .h++ .tcc
- c 预处理文件为:
.i
- c++预处理文件为:
ii
3 gcc 常用指令¶
3.1 gcc预处理、编译、汇编和链接4大步骤¶
- 预处理
-E
:g++ -E xxx.c
,生成xxx.i
预处理文件 - 编译
-S
:g++ -S xxx.i
,生成xxx.s
汇编文件 - 汇编
-c
:g++ -c xxx.s
,生成xxx.o
目标文件 - 链接
g++ xxx.o -o xxx.exe
,生成xxx.exe
可执行文件(输入文件放在g++ 之后,其它选项在输入文件之后,否则可能会包链接错误)
$ ls
1.cpp
$ cat 1.cpp
#include<iostream>
using namespace std;
int main(){
cout<<"hello world!"<<endl;
}
$ g++ -E 1.cpp
$ ls
1.cpp 1.i
$ g++ -S 1.i
$ ls
1.cpp 1.i 1.s
$ g++ -c 1.i
$ ls
1.cpp 1.i 1.o 1.s
$ g++ 1.o -o 1.exe
$ ls
1.cpp 1.exe 1.i 1.o 1.s
$ ./1.exe
hello world!
3.2 -c 选项¶
- 功能:编译或汇编源文件,但不链接。链接阶段根本没有完成。最终输出是每个源文件的对象文件形式。输入的文件可以是
.c/cc
、.i
、.s
,输出则是.o
文件。
3.3 -S 选项¶
- 功能:编译但不汇编。最终输出是汇编文件形式。输入的文件可以是
.c/cc
、.i
,输出则是.s
文件。
3.4 -E 选项¶
- 功能:预处理阶段后停止;输出的是
.i
预处理文件。
3.5 -x 选项¶
- 功能:显示指定输入文件的语言,如
-xc
,-xc++
。
3.6 选项-o¶
- 功能:指定生成文件名
gcc test.c -o test
3.7 选项-s¶
- 功能:删除所有符号和重定位信息删除所有符号和重定位信息。
gcc -s test.c -o test
3.8 选项-L指定静态库查找路径¶
g++ -L~/test libprint.a test.c
3.9 选项-l添加链接库¶
# 优先链接动态库,找不到动态库再尝试链接静态库
g++ -L~/test -lprint test.c
3.10 选项-static指定连接静态库而不是动态库¶
g++ -L~/test -static -lprint test.c
3.11 选项-I指定头文件包含目录¶
g++ -I~/test/src print.c test.c
3.12 选项-g生成调试信息¶
g++ -g test.c
3.13 编译多个文件¶
假设有3个文件:main.cpp print.cpp print.h - 方式一:直接生产可执行程序
$ ls
main.cpp print.cpp print.h
$ g++ main.cpp print.cpp -o main.exe
$ ls
main.cpp main.exe print.cpp print.h
- 方式二:生成目标文件,再链接
$ ls
main.cpp print.cpp print.h
$ g++ -c main.cpp
$ g++ -c print.cpp
$ ls
main.cpp main.o print.cpp print.h print.o
$ g++ main.o print.o -o main.exe
$ ls
main.cpp main.exe main.o print.cpp print.h print.o
3.14 -std=支持标准范围¶
以下是gcc10.0.2支持的c++标准
- -std=c++03
- -std=c++11
- -std=c++14
- -std=c++17
- -std=c++2a
3.15 选项-save-temps保存中间文间(.i .s .o文件)¶
$ ls
main.cpp print.cpp print.h
$ g++ main.cpp print.cpp -save-temps
$ ls
a.out main.ii main.s print.h print.o main.cpp main.o print.cpp print.ii print.s
4 编译选项¶
许多选项都有以“-f”或“-W”开头的长名称,例如,`-fmove-loop-invariants`,`-Wformat`等。其中大多数都有正和负形式;`-ffo`的否定形式是`-fno-foo`,`-Wformat`的否定形式是`-Wno-format`。
4.1 @file¶
- 功能:从指定文件中读取命令行选项, 文件内多个选项用空格分隔;文件本身可能包含其他
@文件
选项;任何这样的选项都将被递归处理。
4.2 -W编译警告¶
-Wall
:打开所有警告-werror
:将警告转为错误(将触发编译报错,中断编译)-w
:关闭所有警告(不建议使用)
4.3 忽略特定警告¶
开启警告通常是 -W
+警告名称,如 Wimplicit
是开启警告,而关闭警告通常是 -Wno-
+警告名称,如 Wno-implicit
。
4.4 -O编译优化¶
编译器提供4个优化等级-O0
、-O1
、-O2
、-O3
,其中-O0
表示没有优化,-O3
表示最高的优化等级
4.5 -D声明宏、-U取消宏¶
-Dmacro
或-D macro
:声明宏,作用同#define macro
-Dmacro=defn
或-D macro=defn
:声明宏,作用同#define macro=defn
-Umacro
或-U macro
:取消宏,作用同#undef macro
-undef
:取消对任何非标准宏的定义
5 编译链接静态库动态库¶
Linux上库命名格式,以lib起始,xxx表示库名,.a结尾是静态库,.so结尾是动态库
- 静态库: libxxx.a
- 动态库: libxxx.so
5.1 c++ 动态库静态库后缀¶
- Linux
- 静态库:
xxx.a
(.a代表archive) - 动态库:
xxx.so
(.so代表share object) - Windows
- 静态库:
xxx.lib
- 动态库:
xxx.dll
5.2 编译和链接静态库¶
-L
选项指定库路径,不指定默认系统/lib,/usr/lib等路径
5.2.1 生成¶
- 将源文件生成目标文件
- 使用ar打包成静态库
$ g++ -c print.cpp
$ ls
main.cpp print.cpp print.h print.o
$ ar rcs libprint.a print.o
$ ls
libprint.a main.cpp print.cpp print.h print.o
5.2.2 使用¶
-static
指定静态链接-lxxx
指定静态库或libxxx.a
注意:-o xxx.exe
应该放入最后,即libxxx.a不应该在xxx.exe后
$ g++ -static main.cpp libprint.a -o main.exe
$ ls
libprint.a main.cpp main.exe print.cpp print.h print.o
shuhaiwen@shuhaiwen-PC:~/code/cpp$ ./main.exe
hello world!
$ g++ main.cpp -o main.exe -static -L /home/shuhaiwen/code/cpp/ -lprint
$ ls
libprint.a main.cpp main.exe print.cpp print.h print.o
shuhaiwen@shuhaiwen-PC:~/code/cpp$ ./main.exe
hello world!
5.3 编译和链接动态库¶
5.3.1 生成¶
-fpic -shared
$ g++ -fpic -shared print.cpp -o libprint.so
$ ls
libprint.a libprint.so main.cpp print.cpp print.h
5.3.2 使用¶
- 方式一:在代码内使用
dlfcn.h
提供的函数动态导入需要的函数 dlopen
:打开动态库dlsym
:获取库函数dlclose
:关闭动态库- 方式二:像静态库链接一样使用
- 将动态库放入系统库目录下(例如 /usr/lib、/usr/lib64、/lib、/lib64)(不推荐)
- 在
.bash_profile
文件中导出LD_LIBRARY_PATH
,如export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库路径
- 加上选项
-Wl,-rpath=[.so路径]
,比如g++ main.cpp -o main.exe -Wl,-rpath=/home/shuhaiwen/code/cpp/ -lprint
6 扩展¶
6.1 ldd命令查看可执行程序依赖的共享库¶
$ ldd libprint.so
linux-vdso.so.1 (0x00007ffd48119000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f94c2660000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f94c24dd000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f94c24c3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f94c2302000)
/lib64/ld-linux-x86-64.so.2 (0x00007f94c2809000)
7 gcc编译错误总结¶
7.1 错误:‘xxx’不是一个类型名¶
- 分析:此类错误就是编译器并没有找到"xxx"类的定义
- 原因:
- 没有包含头文件
- 多个文件中使用了相同的防止头文件重复包含的宏,导致某个头文件没有被包含进来
2 gcc 标准¶
1 c 标准历史版本¶
最初的 ANSI C 标准 (X3.159-1989) 于 1989 年获得批准并于 1990 年发布。该标准于 1990 年晚些时候被批准为 ISO 标准 (ISO/IEC 9899:1990)。
扩展的c标准,比如gnu90、gnu99、gnu11,这些标准时对同级别标准的扩展,比如gnu90就会支持部分c99、c11特性。gcc12.2默认就会开启-std=gnu17。
C89/C90
:(原始版)原始的ansi c标准,gcc 选项使用-ansi
,-std=c90
or-std=iso9899:1990
C94/C95
:(修订版)主要修订C89/C90
标准中的错误,gcc选项使用-std=iso9899:199409
C99
:(新标准)gcc选项使用-std=c99
or-std=iso9899:1999
;1999 年作为 ISO/IEC 9899:1999 发布。(在开发过程中,这个标准版本的草稿被称为 C9X。)C11
:(新标准)gcc选项使用-std=c11
or-std=iso9899:2011
;2011 年作为 ISO/IEC 9899:2011 发布。(在开发过程中,这个标准版本的草案被称为 C1X。)C17
:-std=c17
or-std=iso9899:2017
;2017 年编写了一个集成了更正的版本,并于 2018 年作为 ISO/IEC 9899:2018 发布;它被称为 C17,支持;C2x
:(新标准)gcc选项使用-std=c2x
;C 标准的另一个版本,正在开发中。
2 c++标准历史版本¶
GCC 支持 1998 年发布的原始 ISO C++ 标准,以及 2011、2014、2017 年和大部分 2020 年的修订版。
您还可以使用 -std=gnu++98(对于带有 GNU 扩展的 C++98)或 -std=gnu++11(对于带有 GNU 扩展的 C++11)显式选择 C++ 语言的扩展版本,或 -std=gnu++14(对于带有 GNU 扩展的 C++14),或 -std=gnu++17(对于带有 GNU 扩展的 C++17),或 -std=gnu++20(对于 C+ +20 与 GNU 扩展)。gcc12.2默认开启-std=gnu++17。
C++98
:(原始版)gcc 选项使用-ansi
or-std=c++98
;最初的 ISO C++ 标准作为 ISO 标准 (ISO/IEC 14882:1998) 发布。C++03
:(修订版)gcc 选项使用-std=c++03
;2003 年发布的技术勘误表 (ISO/IEC 14882:2003) 对C++98
进行了修订。C++11
:(新标准)gcc 选项使用-std=c++11
;2011 年发布为 ISO/IEC 14882:2011在发布之前,它通常被称为C++0x
。C++14
:(新标准)gcc 选项使用-std=c++14
;2014 年发布为 ISO/IEC 14882:2014;在发布之前,它有时被称为C++1y
。C++17
:(新标准)gcc 选项使用-std=c++17
;2017 年发布了 ISO/IEC 14882:2017。在发布之前通常被称为C++1z
。C++20
:(新标准)gcc 选项使用-std=c++20
; 2020 年发布为 ISO/IEC 14882:2020;在发布之前,它有时被称为 C++2a。C++23
:又被称为 C++2b。
3 警告选项¶
更多警告选项请参考官网https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Warning-Options.html#Warning-Options
-fsyntax-only
:检查代码是否有语法错误,但除此之外不要做任何事情。-fmax-errors=n
:将错误消息的最大数量限制为 n,此时 GCC 会退出而不是尝试继续处理源代码。如果 n 为 0(默认值),则生成的错误消息的数量没有限制。如果还指定了-Wfatal-errors
,则-Wfatal-errors
优先于该选项。-w
:禁止所有警告信息。-Werror
:将所有警告转换从错误。-Werror=
:将指定的警告转为错误。例如-Werror=switch
将-Wswitch
控制的警告变成错误。-Wno-error=
:用于否定特定的警告。例如使用了-Werror
,再使用-Wno-error=switch
使-Wswitch
警告不是错误,即使使用了-Werror
。-Wfatal-errors
:此选项导致编译器在发生第一个错误时中止编译,而不是尝试继续并打印更多错误消息。-Wall
:开启大部分警告,激活的警告信息如下。
-Waddress -Warray-bounds=1 (only with -O2) -Warray-compare -Warray-parameter=2 (C and Objective-C only) -Wbool-compare -Wbool-operation -Wc++11-compat -Wc++14-compat -Wcatch-value (C++ and Objective-C++ only) -Wchar-subscripts -Wcomment -Wdangling-pointer=2 -Wduplicate-decl-specifier (C and Objective-C only) -Wenum-compare (in C/ObjC; this is on by default in C++) -Wformat -Wformat-overflow -Wformat-truncation -Wint-in-bool-context -Wimplicit (C and Objective-C only) -Wimplicit-int (C and Objective-C only) -Wimplicit-function-declaration (C and Objective-C only) -Winit-self (only for C++) -Wlogical-not-parentheses -Wmain (only for C/ObjC and unless -ffreestanding) -Wmaybe-uninitialized -Wmemset-elt-size -Wmemset-transposed-args -Wmisleading-indentation (only for C/C++) -Wmismatched-dealloc -Wmismatched-new-delete (only for C/C++) -Wmissing-attributes -Wmissing-braces (only for C/ObjC) -Wmultistatement-macros -Wnarrowing (only for C++) -Wnonnull -Wnonnull-compare -Wopenmp-simd -Wparentheses -Wpessimizing-move (only for C++) -Wpointer-sign -Wrange-loop-construct (only for C++) -Wreorder -Wrestrict -Wreturn-type -Wsequence-point -Wsign-compare (only in C++) -Wsizeof-array-div -Wsizeof-pointer-div -Wsizeof-pointer-memaccess -Wstrict-aliasing -Wstrict-overflow=1 -Wswitch -Wtautological-compare -Wtrigraphs -Wuninitialized -Wunknown-pragmas -Wunused-function -Wunused-label -Wunused-value -Wunused-variable -Wuse-after-free=3 -Wvla-parameter (C and Objective-C only) -Wvolatile-register-var -Wzero-length-bounds
-Wextra
or-W
:开启额外的警告(这会启用一些 -Wall 未启用的额外警告标志。),激活的警告信息不仅包括下面部分,还包含以下几种- 使用 <、<=、> 或 >= 将指针与整数零进行比较。
- (仅限 C++)枚举数和非枚举数都出现在条件表达式中。
- (仅限 C++)模棱两可的虚基。
- (仅限 C++)下标已声明为寄存器的数组。
- (仅限 C++)获取已声明为寄存器的变量的地址。
- (仅限 C++)基类未在派生类的复制构造函数中初始化。
-Wclobbered -Wcast-function-type -Wdeprecated-copy (C++ only) -Wempty-body -Wenum-conversion (C only) -Wignored-qualifiers -Wimplicit-fallthrough=3 -Wmissing-field-initializers -Wmissing-parameter-type (C only) -Wold-style-declaration (C only) -Woverride-init -Wsign-compare (C only) -Wstring-compare -Wredundant-move (only for C++) -Wtype-limits -Wuninitialized -Wshift-negative-value (in C++11 to C++17 and in C99 and newer) -Wunused-parameter (only with -Wunused or -Wall) -Wunused-but-set-parameter (only with -Wunused or -Wall)
4 调试选项¶
1. 更多调试选项参考官方文档https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Debugging-Options.html#Debugging-Options
2. 如果您使用多个 -g 选项,无论有无级别编号,只有最后一个选项是有效的。
title: -g和-ggdb区别?
- `-g`:该选项可以利用操作系统的“原生格式(native format)”生成调试信息。GDB 可以直接利用这个信息,其它调试器也可以使用这个调试信息
- `-ggdb`:能使GCC为GDB生成专用的更为丰富的调试信息,但是,此时就不能用其他的调试器来进行调试了 (如 ddx)
- `-g`生成的调试信息是通用的,大多数调试器都能使用,但`-gdb`只支持GDB调试器
-g[level]
、-ggdb[level]
、-gstabs[level]
、-gxcoff[level]
、-gvms[level]
:- level:可选0、1、2、3级别,如果不指定level,默认时2。如
-g
等价于-g2
level 0
:不产生任何调试信息。-g0
否定-g
,也就相当于没有指定-g
选项。(gdb调试时提示no debugging symbols found)level 1
:产生的信息最少,不包含局部变量和与行号有关的调试信息,因此只能够用于回溯跟踪和堆栈转储之用。回溯跟踪指的是监视程序在运行过程中的函数调用历史,堆栈转储则是一种以原始的十六进制格式保存程序执行环境的方法,两者都是经常用到的调试手段。level 2
:允许使用只有 GDB 可以使用的额外调试信息,调试信息包括扩展的符号表、行号、局部
或外部变量信息。(默认级别)level 3
:不仅包括level 2
的所有内容,还支持源代码中定义的宏。
- level:可选0、1、2、3级别,如果不指定level,默认时2。如
-gbtf
:BTF 调试信息。 BTF 是 eBPF 目标的默认调试格式。在其他目标(如 x86)上,当通过各自的命令行选项显式启用两种调试格式时,可以生成 BTF 调试信息以及 DWARF 调试信息。
5 优化选项¶
更多优化选项参考官方文档https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Optimize-Options.html#Optimize-Options
-O0
:不进行优化(默认选项)-O
或-O1
:编译器会尝试减少代码大小和执行时间,但不执行任何需要大量编译时间的优化。-O2
:GCC 执行几乎所有支持的优化(不涉及空间速度)。-O3
:更近一步的优化-Os
:优化大小。 -Os 启用所有 -O2 优化,除了那些经常增加代码大小的优化:-Og
:优化调试体验。对于生成可调试代码来说,它是比 -O0 更好的选择,因为在 -O0 处禁用了一些收集调试信息的编译器通道。否则 -Og 启用所有 -O1 优化标志,除了那些可能干扰调试的标志。(调试建议选择)
6 环境变量¶
6.1 C_INCLUDE_PATH、CPLUS_INCLUDE_PATH、CPATH¶
C_INCLUDE_PATH
、CPLUS_INCLUDE_PATH
、CPATH
环境变量用于 C/C++ 搜索 include 目录C_INCLUDE_PATH
只对预处理 C语言有效,CPLUS_INCLUDE_PATH
只对预处理 C++ 有效CPATH
对两者都有效。