目录
(7)gcc/g++ 优化标识 -O1 -O2 -O3 -Os -Ofast -Og的作用
(1)c/c++编译过程
gcc编译C源码有四个步骤:
- 预处理(也叫预编译):宏定义展开、头文件展开、条件编译等,同时将代码中的注释删除,这里并不会检查语法。
- 编译:检查语法,将预处理后文件编译生成汇编文件。在这个阶段中,gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。
- 汇编:将汇编文件生成目标文件(二进制文件)。
- 链接:C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行程序中去。
(2)gcc/g++常用参数
选项 | 含义 |
-o 可执行文件名 | 指定生成的输出文件名,比如可执行文件名或者二进制文件名等 |
-E | 只运行 C 预编译器,即只执行头文件展开、宏替换和注释去掉的工作:gcc -E hello.c -o hello.i |
-S | c文件变成汇编文件(将C代码转换为汇编代码):gcc -S hello.i -o hello.s |
-c | 编译但是不链接。只编译并生成目标文件。gcc -c hello.s -o hello.o |
-std=c++11 | 语言标准,如-std=c++11表示采用2011 ISO C++标准 |
-g | 在目标文件中嵌入调试信息,生成供调试用的可执行文件,以便gdb之类的调试程序调试,GNU 调试器可利用该信息。由于文件中包含了调试信息因此运行效率很低,且文件也大不少,因此一般调试时可加此选项,正式发布时可不加。 |
-O1 | 编译器优化级别控制。默认不做优化,即-O0。使用-O1, -O2, -O3,编译器优化程度依次增加,优化越高,编译时用时越长,但运行时效率越高。 |
-Wall | 打开所有警告,生成所有警告信息 |
-w | 不生成任何警告信息。 |
-shared | 生成共享目标文件。生成动态库时需要用到。 |
-static | 禁止使用动态库,优先使用动态库。 |
-I dir | 大写的i。指定头文件所在的文件夹。dir是路径,表示把dir加到头文件搜索路径中。这个选项很重要,特别是在使用第三方库的时候,以及cpp文件与h文件不再同一个路径下的时候 |
-l | -l,小写的l,指定所使用到的函数库名。 链接到某个库,比如-lnetcdf_c++4 |
-L dir | -L,大写的l,指定函数库所在的文件夹。可以与-l配合使用,告诉编译器去哪里找到链接库 |
-fPIC | fPIC 的全称是 Position Independent Code, 用于生成位置无关代码。可以大写-fPIC,也可以小写-fpic。 (1)不加 fPIC 也可以生成 .so 文件,但是对于源文件有要求,例如因为不加 fPIC 编译的 so 必须要在加载到用户程序的地址空间时重定向所有表目,所以在它里面不能引用其它地方的代码。因此建议编译时加上这个选项。 (2)不用此选项的话编译后的文件(比如二进制目标文件、动态库文件等)是位置相关的,所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。 (3)不加这个选项更消耗内存,但是不加 fPIC 编译的 so 文件的优点是加载速度比较快。 |
-D 宏名 | 相当于给程序中加了一个宏 比如-D name ,预定义一个名为name 的宏,值为1。 比如-D DEBUG,预定义一个名为DEBUG的宏,值为1。 -Dname =definition,预定义名为name ,值为definition 的宏。 |
(3)编译举例
第1种:四步合一
直接由源代码编译得到可执行文件
单个c文件:gcc main.c -o main
多个c文件:gcc main.c data.c tool.c -o main
第2种:源文件→二进制文件→可执行文件
# 由cpp文件汇编生成的二进制目标文件
g++ -c -O2 -fPIC DataTool.cpp -o DataTool.o
g++ -c -O2 -fPIC DealTool.cpp -o DealTool.o
g++ -c -O2 -fPIC main.cpp -o main.o
# 编译生成test_demo
g++ -O2 -fPIC main.o DataTool.o DealTool.o -o test_demo -ldl -lrt
(4)gcc常连接的动态库
-lz 是link libz,链接压缩库(Z)
-lrt 实时库(real time):shm_open系列
-lm 是link libm,链接数学库(math)
-lc 是link libc,链接标准C库(C lib)
-ldl 是显式加载动态库的动态函数库,如果你的程序中使用dlopen、dlclose等
-lnsl 需要链接libnsl.so, libnsl.so是C网络服务的库
-lclntsh 需要链接libclntsh.so,是orcale客户端编译的时候需要用到的一个库
-lpthread 线程库
-lsnmp++ 网络管理库
-locci 是指Oracle C++ Call Interface对应的动态库,(OCCI)驱动程序
(5)linux下cc与gcc的关系
linux系统的C编译器为gcc,它是GNU推出的功能强大的编译工具,因为UNIX系统的编译器为cc,所以在Linux系统还保留一个链接cc,用于和UNIX的向后兼容。
gcc是GNU C Compiler的缩写。gcc(GNU C Compiler)编译器的作者是Richard Stallman,也是GNU项目的奠基者。
(6)gcc和g++的区别
(1)常用习惯与误区说明
值得一提的是,实际使用中我们更习惯使用 gcc 指令编译 C 语言程序,用 g++ 指令编译 C++ 代码。需要强调的一点是,这并不是 gcc 和 g++ 的区别,gcc 指令也可以用来编译 C++ 程序,同样 g++ 指令也可以用于编译 C 语言程序。
(2)编译c或者cpp时的区别
- 对于gcc来说,后缀为.c的,gcc 把它当作是 c程序,后缀为.cpp 的,会认为是C++程序。
- 对于g++来说,无论是.c还是.cpp,都当做c++程序。
gcc和g++都可以编译c++代码
- 直接用g++
- 用gcc,但是需要链接c++标准库:gcc -lstdc++。
(3)是否定义__cplusplus宏
- __cplusplus这个宏只是标志着编译器将会把代码按c还是C++语法来解释。
- gcc不会定义__cplusplus 宏,而g++会。如果后缀为.c,并且采用gcc编译器,则该宏就是未定义的,否则如果是g++,就是已定义。
(7)gcc/g++ 优化标识 -O1 -O2 -O3 -Os -Ofast -Og的作用
(1)-O,-O1
这两个命令的效果是一样的,目的都是在不影响编译速度的前提下,尽量采用一些优化算法降低代码大小和可执行代码的运行速度。并开启如下的优化选项:
-fauto-inc-dec
-fbranch-count-reg
-fcombine-stack-adjustments
-fcompare-elim
-fcprop-registers
-fdce
-fdefer-pop
-fdelayed-branch
-fdse
-fforward-propagate
-fguess-branch-probability
-fif-conversion2
-fif-conversion
-finline-functions-called-once
-fipa-pure-const
-fipa-profile
-fipa-reference
-fmerge-constants
-fmove-loop-invariants
-freorder-blocks
-fshrink-wrap
-fshrink-wrap-separate
-fsplit-wide-types
-fssa-backprop
-fssa-phiopt
-fstore-merging
-ftree-bit-ccp
-ftree-ccp
-ftree-ch
-ftree-coalesce-vars
-ftree-copy-prop
-ftree-dce
-ftree-dominator-opts
-ftree-dse
-ftree-forwprop
-ftree-fre
-ftree-phiprop
-ftree-sink
-ftree-slsr
-ftree-sra
-ftree-pta
-ftree-ter
-funit-at-a-time
(2)-O2
该优化选项会牺牲部分编译速度,除了执行-O1所执行的所有优化之外,还会采用几乎所有的目标配置支持的优化算法,用以提高目标代码的运行速度
-fthread-jumps
-falign-functions -falign-jumps
-falign-loops -falign-labels
-fcaller-saves
-fcrossjumping
-fcse-follow-jumps -fcse-skip-blocks
-fdelete-null-pointer-checks
-fdevirtualize -fdevirtualize-speculatively
-fexpensive-optimizations
-fgcse -fgcse-lm
-fhoist-adjacent-loads
-finline-small-functions
-findirect-inlining
-fipa-cp
-fipa-cp-alignment
-fipa-bit-cp
-fipa-sra
-fipa-icf
-fisolate-erroneous-paths-dereference
-flra-remat
-foptimize-sibling-calls
-foptimize-strlen
-fpartial-inlining
-fpeephole2
-freorder-blocks-algorithm=stc
-freorder-blocks-and-partition -freorder-functions
-frerun-cse-after-loop
-fsched-interblock -fsched-spec
-fschedule-insns -fschedule-insns2
-fstrict-aliasing -fstrict-overflow
-ftree-builtin-call-dce
-ftree-switch-conversion -ftree-tail-merge
-fcode-hoisting
-ftree-pre
-ftree-vrp
-fipa-ra
(3)-O3
该选项除了执行-O2所有的优化选项之外,一般都是采取很多向量化算法,提高代码的并行执行程度,利用现代CPU中的流水线,Cache等
-finline-functions // 采用一些启发式算法对函数进行内联
-funswitch-loops // 执行循环unswitch变换
-fpredictive-commoning //
-fgcse-after-reload //执行全局的共同子表达式消除
-ftree-loop-vectorize //
-ftree-loop-distribute-patterns
-fsplit-paths
-ftree-slp-vectorize
-fvect-cost-model
-ftree-partial-pre
-fpeel-loops
-fipa-cp-clone options
(4)-Os
这个优化标识和-O3有异曲同工之妙,当然两者的目标不一样,-O3的目标是宁愿增加目标代码的大小,也要拼命的提高运行速度,但是这个选项是在-O2的基础之上,尽量的降低目标代码的大小,这对于存储容量很小的设备来说非常重要。为了降低目标代码大小,会禁用下列优化选项,一般就是压缩内存中的对齐空白(alignment padding)
(5)-Ofast
该选项将不会严格遵循语言标准,除了启用所有的-O3优化选项之外,也会针对某些语言启用部分优化。如:-ffast-math ,对于Fortran语言,还会启用下列选项
-fno-protect-parens
-fstack-arrays
(6)-Og
优化调试体验。 -Og应该是标准edit-compile-debug周期的优化级别选择,在保持快速编译和良好调试体验的同时,提供合理的优化级别。用于生成可调试代码,因为某些收集调试信息的编译器通道在以下位置被禁用 -O0。像-O0 -Og完全禁用了许多优化过程,因此控制它们的单个选项无效。除此以外-Og 使所有 -O1 优化标志,但那些可能会干扰调试的标志除外:
-fbranch-count-reg
-fdelayed-branch
-fdse
-fif-conversion
-fif-conversion2
-finline-functions-called-once
-fmove-loop-invariants
-fssa-phiopt
-ftree-bit-ccp
-ftree-dse
-ftree-pta
-ftree-sra
(8)编译常见报错解决方案
1.gcc编译c++程序报错
Linux环境C++编译报错:undefined reference to `std::ios_base::Init::Init()
问题描述
/root/gcc11.3/include/c++/11.3.0/iostream:74:对‘std::ios_base::Init::Init()’未定义的引用
/root/gcc11.3/include/c++/11.3.0/iostream:74:对‘std::ios_base::Init::~Init()’未定义的引用
Linux Linux环境C++编译报错 undefined reference to `std::i
出现情况:在Linux系统,用gcc(C编译器)编译C++程序,会报标题的错误。
原因是用gcc编译c++程序时,应当链接的库文件为libstdc++.so,而不是默认的libc.so,因此需要用-lstdc++参数指明,否则会在链接时发生错误。
如: gcc myfirst.cpp -lstdc++
gcc mem_map.cpp mmap_w.cpp -o mmap_w -lstdc++
或者使用g++命令
g++ mem_map.cpp mmap_w.cpp -o mmap_w
2.编译c++出现对xxx未定义的引用
uttools-libhelper.cxx:(.text+0x23cd):对‘utadapter::ut_initxxxxx未定义的引用
collect2: 错误:ld 返回 1
总结一下C++链接错误“对函数或变量未定义的引用”解决办法:
- 函数或变量所在的库,没有添加到link_directories,需要在CMakeLists.txt里面添加。
- 模板函数或者模板类数据没有实例化,也可能导致这个问题,需要指定模板,对模板函数或模板类做实例化。
- src子文件夹下的文件需要在CMakeLists.txt里面include,也就是说有可能executable文件link到source或者include文件时没有找到函数或变量所在的文件,所以说它们未定义。因此将函数或变量所在文件添加到source或者include中并link到可执行文件,即可解决。
- 有时可能是需要连接的库是软链接,需要先重新生成软键,再进行编译。
- c++类成员函数的定义时未加作用域。
3.找不到对应的线程库
undefined reference to symbol 'pthread_create@@GLIBC_2.2.5'
这个错误信息通常表示在链接时找不到对应的线程库。解决办法是在编译时添加 -pthread 选项来链接线程库。以下是一个示例代码:
gcc -pthread your_code.c -o your_output
4.cannot find -lstdc++
/usr/bin/ld: cannot find -lstdc++
需要安装gcc和g++
5.linux下动态库名字格式不对
动态库名字格式 libxxx.so
(9)运行可执行文件常见问题解决办法
1.某版本的CXXABI找不到
/lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by............)
问题描述
原因分析
gcc动态库版本过老
解决办法
a. 查看gcc动态库版本
strings /lib64/libstdc++.so.6 | grep CXXABI
发现CXXABI最高版本只有1.3.7,没有1.3.9的。解决办法是高版本gcc的动态库替换老版本gcc的动态库。
查看libstdc++.so.6属性
ls -l /lib64/libstdc++.so.6
发现libstdc++.so.6是个软连接,是直接链接到libstdc++.so.6.0.19上去的,而libstdc++.so.6.0.19是老版本的动态库,所以下面要做的就是将libstdc++.so.6连接到新版本的动态库上去。
b.查找编译gcc时生成的最新动态库
执行以下命令,查找编译gcc时生成的最新动态库
find / -name "libstdc++.so.*"
其中/root/gcc11.3/lib64/libstdc++.so.6.0.29是我们新版本的动态库
查看其动态库,能否满足我们的要求
strings /root/gcc11.3/lib64/libstdc++.so.6.0.29 | grep CXXABI
可以看到CXXABI有1.3.9的,满足要求。
如果系统中没有相应的libstdc++.so.x.x.x文件或者不满足我们的要求,可以去下最新版本的RPM resource libstdc
http://www.rpmfind.net/linux/rpm2html/search.php?query=libstdc&submit=Search+...&system=centos&arch=
c. 将上面的最新动态库复制到/lib64/目录下
cp /root/gcc11.3/lib64/libstdc++.so.6.0.29 /lib64/
d. 重新将libstdc++.so.6连接到libstdc++.so.6.0.29上去
cd /lib64
rm -rf libstdc++.so.6
ln -s libstdc++.so.6.0.29 libstdc++.so.6
e. 默认动态库升级完成。重新运行以下命令检查动态库
strings /lib64/libstdc++.so.6 | grep 'CXXABI'
可以看到新的动态库确实链接上去了
2.调用动态库找不到.so库
问题提示
linux下调用动态库.so文件时提示:cannot open shared object file: No such file or directory
在使用第三方so库的时候需要引入到自己的项目中,在编译阶段是没问题的,但是一旦运行会报以下错误:error while loading shared libraries: xxxxxxx.so: cannot open shared object file: No such file or directory
查看该可执行文件需要的库是否存在
比如可执行文件名字叫test
ldd test
执行以上命令,它需要调用哪些动态库,以及该动态库是否存在
解决办法
设置LD_LIBRARY_PATH这个环境变量
由于操作系统在运行程序过程中的package搜索路径依赖于LD_LIBRARY_PATH这个环境变量,所以检查是否包括该路径:
export | grep LD_LIBRARY_PATH
如果没有,则添加:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库所在目录路径
3.缺少libcrypto.so.10库
问题提示
mongo: error while loading shared libraries: libcrypto.so.10: cannot open shared object file: No such file or directory
查看缺少的库文件
ldd /data/usr/mongodb/bin/mongod
/lib64 目录下少了文件:libcrypto.so.10、libssl.so.10
查看宿主机:
ll /lib64/libcrypto.so.1*
ll /lib64/libssl.so*
发现libcrypto.so.10是软连接,真正的文件是libcrypto.so.1.0.2k
解决方案
第①种:创建软链接
如果有libcrypto.so.1.0.2k 则在容器中创建软连接就可以了
第②种:直接复制过来
复制文件到当前目录。
懒得复制原名再创建软连接了,直接复制成新的名称
cp /lib64/libcrypto.so.1.0.2k libcrypto.so.10
cp /lib64/llibssl.so.1.0.2k libssl.so.10
4.file too short问题
问题描述
./testAPI: error while loading shared libraries: libNanoQuote.so: file too short
原因:拷贝的libNanoQuote.so的文件大小是0,于是出现了这个问题
解决办法:将正确大小的动态库libNanoQuote.so文件拷贝过来就好
end