gcc 和 g++ 的区别
-
gcc
是 GCC 编译器的通用编译指令。根据文件的后缀名,gcc
指令可以自行判断出编程语言的类别,比如xxx.c
默认以 C 语言程序的方式编译,xxx.cpp
默认以 C++ 语言程序的方式编译当然,
gcc
指令也提供了手动指定代表编译方式的接口,即使用-x
选项。gcc -xc++ xxx.c
即表示以 C++ 代码的方式编译xxx.cpp
文件。g++
指令无论文件的后缀名是什么,都一律按照编译 C++ 代码的方式编译。 -
单纯的
gcc
命令无法自动链接 C++ 标准库文件,需手动为其添加-lstdc++ -shared-libgcc
选项。g++
指令可以认为等同于gcc -xc++ -lstdc++ -shared-libgcc
编译过程
一个 gcc 的编译过程大致如上图。可以分为以下几步:预处理、编译、汇编、链接
预处理
预处理规则:
- 处理源代码文件中以
#
开始的预编译指令 - 删除所有的注释
//
和/* */
- 添加行号和文件名标识,比如
#2 "hello.c" 2
,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能显示行号。__LINE__
这个宏 ,在这个阶段就已经生效 - 保留所有的
#pragma
编译器指令,因为编译器要使用它们。
预处理作用:确定宏或者头文件包含是否正确。当我们无法确认时,可以通过预处理后的文件来确定问题。
预编译用法:使用 gcc
的 -E
选项(默认情况下 gcc -E
只会将预处理的结果输出到屏幕上,并不会自动保存到某个文件。因此该指令往往会和 -o
选项连用)
gcc -E hello.c -o hello.i
经过预编译后,会得到一个 .i
文件,这个文件不包含任何宏定义,所有的宏都已经被展开,并且包含的头文件也被插入到 .i
文件中。
预处理过程常用的编译选项:GCC -E选项:对源程序做预处理操作 (biancheng.net)
编译
编译过程就是把预处理完的文件进行一些列词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。
用法:使用 gcc
的 -S
选项
gcc -S hello.i -o hello.s
注意:gcc -S
也处理的不必须是经过预处理的 .i
文件,其功能是“将指定文件处理至编译阶段结束”,因此其也可以操作源代码文件:
gcc -S demo.c -o demo.s
如果想提高汇编代码的可读性,可以借助 -fverbose-asm
选项,GCC 编译器会为汇编代码添加必要的注释:
gcc -S demo.c -fverbose-asm
汇编
汇编是将汇编代码转变成机器可以执行的指令。大部分汇编语句对应一条机器指令,有的汇编语句对应多条机器指令。
用法:可以调用汇编器 as
或者使用 gcc
的 -c
选项。
as hello.s -o hello.o
gcc -c hello.s -o hello.o
经过汇编会得到目标文件 .o
(Object File)
注意,和 gcc -S
类似,gcc -c
选项并非只能用于加工 .s
文件。事实上,-c
选项是“令 GCC 编译器将指定文件加工至汇编阶段,但不执行链接操作”。
链接
链接过程,是一个比较复杂的过程。链接过程主要包括了以下步骤:
- 地址和空间分配(Address and Storage Allocation)
- 符合决议(Symbol Resolution)
- 重定位(Relocation)
符合决议有时候又叫做符号绑定(Symbol Binding)、名称绑定(Name Binding)。”决议“更倾向于静态链接,”绑定“更倾向于动态链接。我们通过
gcc -v
可以打印编译的详细信息。
关于链接的详细过程参见:彻底理解链接器:一,概念 - SegmentFault 思否
总结
现代版的 gcc
把预编译和编译两个步骤合并成一个步骤,使用一个叫做 cc1
的程序来完成这两个步骤。对于 C++ 来说是 cc1plus
。所以实际上 gcc
这个命令只是这些后台程序的包装,它会根据不同的参数要求去调用预编译程序 cc1
、汇编器 as
、链接器 ld
。
GCC 编译选项
编译选项 | 功 能 |
---|---|
-E |
预处理指定的源文件,不进行编译。 |
-S |
编译指定的源文件,但是不进行汇编。 |
-c |
编译、汇编指定的源文件,但是不进行链接。 |
-o |
指定生成文件的文件名。 |
-g |
生成调试信息。GNU 调试器可利用该信息。 |
-Olevel |
-O0 不进行优化处理;-O / -O1 优化生成代码;-O2 进一步优化。-O3 比 -O2 更进一步优化,包括 inline 函数。 |
-w /-Wall |
不生成任何警告信息/ 生成所有警告信息 |
-Dmacro |
定义宏,默认宏的内容是1。gcc -DDEBUG 常用来选择开启DEBUG宏调试;gcc -DNAME=Peter 定义宏并指定内容。 |
-Umacro |
取消宏定义,作用正好和-D 相反 |
-Idir |
把 dir 加到头文件的搜索路径中,而且 gcc 会在搜索标准头文件之前先搜索 dir |
-Ldir |
把 dir 加到库文件的搜索路径中,而且 gcc 会在搜索标准库文件之前先搜索 dir |
-llib |
指定链接环节中可以调用的库文件,比如 -lstdc++ 。 |
-ansi |
对于 C 语言程序来说,其等价于 -std=c90 ;对于 C++ 程序来说,其等价于 -std=c++98 。这一选项将禁止 GNU C 的某些特色。 |
-std= |
手动指令编程语言所遵循的标准,例如 c89、c90、c++98、c++11 等。 |
-wl |
|
-fPIC |
使编译阶段产生位置无关代码(Position-Independent Code)。 |
-fpermissive |
该选项会将不一致代码的诊断从错误降级为警告。最好不要使用,因为会降低对于代码检查的严格性。 |
-fPIC
-fPIC
产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
gcc -shared -fPIC -o 1.so 1.c
如果不加-fPIC
,则加载 .so
文件的代码段时,代码段引用的数据对象需要重定位,重定位会修改代码段的内容,这就造成每个使用这个 .so
文件代码段的进程在内核里都会生成这个 .so
文件代码段的 copy。每个 copy 都不一样,取决于这个 .so
文件代码段和数据段内存映射的位置。
-l 常用的库
Linux 的库命名是一致的,一般为 libxxx.so
,libxxx.a
或libxxx.la
,那么你要链接某个库就用 -lxxx
即可。常用 -l
指定的一些库:
-ldl
:动态链接函数库#include <dlfcn.h>
-lm
:数学库#include <math.h>
-lc
:标准 C 库-lz
:压缩库-lrt
:实时库(real time)lpthread
:posix 线程库
-I,-L 和 -l 的区别
聊聊gcc参数中的-I, -L和-l_认知 行动 坚持-CSDN博客
GCC 的错误类型及对策
gcc给出的错误资讯一般可以分为四大类,下面我们分别讨论其产生的原因和对策。
语法错误
错误提示∶文件 source.c 中第 n 行有语法错误(syntex errror)。这种类型的错误,一般都是语法错误,应该仔细检查源代码文件,有时也需要对该文件所包含的头文件进行检查。有些情况下,一个很简单的语法错误,GCC 会给出一大堆错误,我们最主要的是要保持清醒的头脑,不要被其吓倒。
头文件错误
错误提示∶找不到头文件 head.h(Can not find include file head.h)。这类错误是源文件中的包含头文件有问题,可能的原因有头文件名错误、指定的头文件所在目录名错误等,也可能是错误地使用了双引号和尖括号。
档案库错误
错误提示∶连接程序找不到所需的函数库,例如∶ld: -lm: No such file or directory
这类错误是与目标文件相链接的函数库有错误,可能的原因是函数库名错误、指定的函数库所在目录名称错误等,检查的方法是使用 find 命令在可能的目录中寻找相应的函数库名,确定档案库及目录的名称并修改程序中及编译选项中的名称。
未定义符号
错误提示∶有未定义的符号(Undefined symbol)。这类错误是在连接过程中出现的,可能有两种原因∶
- 使用者自己定义的函数或者全局变量所在源代码文件,没有被编译、链接,或者干脆还没有定义,这需要使用者根据实际情况修改源程序,给出全局变量或者函数的定义
- 未定义的符号是一个标准的库函数,在源程序中使用了该库函数,而链接过程中还没有给定相应的函数库的名称,或者是该档案库的目录名称有问题,这时需要使用档案库维护命令
ar
检查我们需要的库函数到底位于哪一个函数库中,确定之后,修改 gcc 连接选项中的-l
和-L
项。
参考资料
gcc和g++是什么,有什么区别? (biancheng.net)
彻底理解链接器:一,概念 - SegmentFault 思否
gcc及其选项详解-zhenhuaqin-ChinaUnix博客
gcc编译参数-fPIC的一些问题_老徐_新浪博客 (sina.com.cn)
聊聊gcc参数中的-I, -L和-l_认知 行动 坚持-CSDN博客
本文由 晓楼 创作,采用 知识共享署名4.0 国际许可协议进行许可。本站文章除注明转载/出处外,均为本站原创或翻译,转载前注明出处