服务器正文21:不同编译器对预编译的处理(简单介绍msvc和gcc)及常用gcc编译指令、跨平台debug、release不同指令flag选择

一、背景

  • 大部分编译器都支持预编译
    在的大部分c/c++编译器都是支持预编译头的,例如:gcc,clang,msvc等
  • 预编译作用
    用于优化c++代码的编译速度,毕竟c++的头文件如果包含了模板定义的话,编译速度是很慢的
  • 预编译的做法
    能够吧大部分通用的头文件放置在一个header.h中,在其他源码编译之前预先对其进行编译,之后的代码都能重用这部分预编译头,就可以极大程度上减少频繁的头文件冗余编译。

二、msvc的预编译头处理

预编译头在msvc的项目中很常见,经常会看到类似stdafx.cpp, stdafx.h的文件,就是用于此目的,而msvc编译器是通过编译stdafx.cpp来生成预编译头文件stdafx.pch的。

1)总结

一般同一项目下的stdafx.cpp是创建预编译头(/Yc),其他.cpp文件是使用预编译头(/Yu),所有.h文件不关心这个

2)具体使用

  • 总体流程预览
    1)创建预编译头命令
    2)其他文件使用这个stdafx.pch的方法
    3)链接obj的注意
    4)与其他编译器的区别

  • 创建预编译头命令

$ cl.exe -c -Yc -Fpstdafx.pch -Fostdafx.obj stdafx.cpp

1-Yc就是创建预编译头stdafx.pch
2-Fp来指定*.pch的输出文件路径
3-Fo指定编译stdafx.cpp生成对象文件
  • 其他文件如何使用这个stdafx.pch?

通过将stdafx.h传入-Yu来告诉编译器,编译当前代码,忽略#include “stdafx.h”,直接使用已经编译好的stdafx.pch文件。

cl.exe -c -Yustdafx.h -Fpstdafx.pch -Fotest.obj test.cpp
  • 最后链接的时候
    需要把:stdafx.obj, test.obj都连接上才行,这个也是和gcc, clang编译器不同的地方。
link.exe -out:test test.obj stdafx.obj
  • 其他注意点
    1)注:一定要吧stdafx.obj也链接上哦,虽然stdafx.cpp仅用于生成stdafx.pch,但是对象文件也是需要。
    2)还有个跟gcc, clang有区别的地方是,msvc的-Yu指定stdafx.h必须是#include "stdafx.h"中的头文件名字,不是文件路径哦。

三、gcc的预编译头文件处理

1)gcc搜索stdafx.pch文件路径的规则

1)从stdafx.h所在目录中,查找stdafx.h.pch文件是否存在
2)从-I的头文件搜索路径找查找stdafx.h.pch

2)编译

  • 总体流程
1)编译头文件生成pch文件:
2)使用预编译头文件:
  • 编译头文件生成pch文件:
gcc -c -o stdafx.pch stdafx.h
  • 使用预编译头文件:
gcc -c -include stdafx.h -o test.o test.cpp

四、备注(C和C++对于*.h编译的区别)

1)gcc、clang对于*.h的头文件编译,默认是作为c预编译头来使用的,这跟c++的pch是不一样的。
2)*.h的头文件编译无法给c++的代码使用,如果要生成c++可用的pch文件,必须要告诉编译器,如何去编译stdafx.h

  • 方法
    这个可以通过-x c+±header参数来解决:
gcc -c -x c++-header -o stdafx.pch stdafx.h
  • 当然也可以通过修改后缀名来解决:
gcc -c -o stdafx.pch stdafx.hpp

五、常用gcc和g++编译指令(放在cmake里面使用)

1)常见编译指令

  • 具体编译指令和使用例子
1)-x: 设定文件所使用的语言,使文件后缀名无效
gcc -c -x c++ test.jpg(执行完后生成test.o,这里会忽略文件名字)

2)-c: 只编译生成目标文件即*.o,只编译不链接生成可执行文件,只有.o文件,没有
执行文件
gcc -c test.cpp

3)-S: 把文件编译成为汇编源文件,如下,执行完后生成test.s
gcc -S test.cpp

4)-o: 指定生成的可执行文件,输出文件名称,gcc编译出来的文件缺省的是
a.out,如下,执行完后生成test执行文件
gcc -o test -c test.cpp

5) -ansi: 关闭gnu c中与ansi c不兼容的特性,激活ansi c的专有特性(包括禁止一些
asm inline typeof关键字,以及UNIX,vax等预处理宏),仅支持ANSI标准的C语法,如
下
gcc -ansi -c test.cpp

6)-fno-asm: 禁止将asm,inline和typeof用作关键字,如下
gcc -fno-asm -c test.cpp

7)-funsigned-char或-fno-signed-char: 对char类型进行设置,将char类型设置成
unsigned char,如下
gcc -funsigned-char -c test.cpp
gcc -fno-signed-char -c test.cpp

8)-fsigned-char或-fno-unsigned-char: 对char类型进行设置,将char类型设置成
signed char,如下
gcc -fsigned-char -c test.cpp
gcc -fno-unsigned-char -c test.cpp

9)-include file_name: 在编译test.cpp文件需要aa.hpp文件的时候使用,而此时
test.cpp文件中并没有#include "../aa.hpp",相当于在代码中使用#include "../aa.hpp",
如下:
gcc -c test.cpp -include ../aa.hpp

10) -Dmacro_name: 相当于C语言中的#define macro_name,如下
gcc -DTEST_MAIN -c test.cpp  //#define  TEST_MAIN

11)-Dmacro_name=value: 相当于C语言中的#define macro_name value,如下
gcc -DTEST_MAIN=2 -c test.cpp

12)-Umacro_name: 相当于C语言中的#undef macro_name,如下
gcc -Umax -c test.cpp  //#undef max

13)-Idir_name: 在test.cpp文件中#include "aa.hpp"文件,而aa.hpp又没有和
test.cpp在同一目录下时使用,gcc/g++会到-I指定的目录下查找,即指定额外的头
文件搜索路径,如下
gcc -c test.cpp -I../

14)-dirafter dir_name: 如果-I指定的目录查找失败,将到这个目录里查找,如下
gcc -c test.cpp -I../../ -idirafter ../

15)-nostdinc: 使编译器不再系统缺省的目录里查找头文件,一般和-I一起使用,明
确限定头文件的位置(这里一般用cmake自带的查找目录)
gcc -c test.cpp -I../ -nostdinc

16)-C: 在预处理的时候,不删除注释信息,如下
gcc -C -c test.cpp -I../

17)-M: 生成文件关联信息,目标文件依赖的所有文件,如下
gcc -M -c test.cpp -I../

18)-MM: 生成文件关联信息,与-M相似,但是它将忽略由#include <file>造成的依
赖关系,如下:
gcc -MM -c test.cpp -I../

19)-Wa,option: 此选项传递option给汇编程序;如果option中间有逗号,就将option分
成多个选项,然后传递给会汇编程序
20)-Wl.option: 此选项传递option给链接程序;如果option中间有逗号,就将option分
成多个选项,然后传递给会链接程序
21)-llibrary_name: 指定链接时搜索指定的库,如下
gcc -c test.cpp -I../ -lpthread

22)-Ldir: 指定额外的库搜索路径,如下
gcc -c test.cpp -I../ -L../

23)-O0或-O1或-O2或-O3:编译器优化选项的4个级别,-O0表示没有优化,-O1
为缺省值,-O3优化级别最高,如下
gcc -O3 -c test.cpp -I../ -L../

>备注:
>-O0:无优化(默认) 。 -O和-O1:优化可执行文件大小及执行时间,
>且不使编译时间明显增加。编译大型程序时会显著增加编译时内存的
>使用。

 -O2:包含-O1的优化,并增加无需在目标文件大小和执行速
>度上进行折衷的优化。编译器不执行循环展开及函数内联,即不进行
>“空间换时间”的优化。该选项将增加编译时间和目标文件的执行性
>能。-Os:执行所有不增加目标文件大小的-O2选项,并执行专门减小目
>标文件大小的优化选项。
> 
-O3: 打开所有-O2优化选项并且增加 -
>finline-functions, -funswitch-loops, -fpredictive-commoning, -fgcse-
>after-reload和-ftree-vectorize优化选项。[e.g.1]gcc -O1 source.c -o 
>exec


24)-g: 指示编译器在编译的时候产生调试信息,如下
gcc -O3 -g -c test.cpp -I../ -L../

25)-gstabs: 此选项以stabs格式生成调试信息,但是不包括gdb调试信息,如下
gcc -O3 -gstabs -c test.cpp -I../ -L../

26)-gstabs+: 此选项以stabs格式生成调试信息,并且包含仅供gdb使用的额外调
试信息,如下
gcc -O3 -gstabs+ -c test.cpp -I../ -L../

27)-ggdb: 此选项将尽可能的生成gdb可以使用的调试信息,如下
gcc -O3 -ggdb -c test.cpp -I../ -L../

28)-static: 此选项将禁止使用动态库,一般编译出来的文件比较大,不需要什么动
态链接库就可以运行,如下
cc -static -o test -c test.cpp -I../

29)-shared: 此选项将尽可能使用动态库,一般编译出来的文件比较小,如下
gcc -shared -o test -c test.cpp -I../

30)-tradional: 试图让编译器支持传统的C语言特性,如下
gcc -traditional -o test -c test.cpp -I../

31)-E: 预编译后停下来,生成后缀为*.i的预编译文件,如下
gcc -E -o test.i test.cpp -I../

32)-w: 不生成任何警告信息,如下
gcc -w -o test -c test.cpp -I../

33) -Wall: 生成所有警告信息,如下
gcc -Wall -o test -c test.cpp -I../

34)-pedantic: 当gcc在编译不符合ANSI/ISO C语言标准的源代码时,
将产生相应的警告信息,如下
gcc -pedantic -o test test.cpp

35)-Werror: 要求gcc将所有的警告信息当成错误进行处理,如下
gcc -Werror -o test test.cpp

36)-Wcast-align: 当源程序中地址不需要对齐的指针指向一个地址需
要对齐的变量地址时则产生一个警告,如下
gcc -Wcast-align -o test test.cpp

37)-p或-pg: 会将剖析(profiling)信息加入到最终生成的二进制文件
中,剖析信息对于找出程序的性能瓶颈很有帮助,如下
gcc -p -o test test.cpp
gcc -pg -o test test.cpp

38)-save-temps: 保存编译过程中生成的一些中间文件,如test.s, 
test.ii,如下
gcc -save-temps -o test test.cpp

39)-pipe: 使用管道代替编译中临时文件,如下
gcc -pipe -o test test.cpp

40)-ftime-report: 统计编译消耗的时间并显示报告,如下
gcc -ftime-report -o test test.cpp

41)-fmem-report: 显示所有的静态内存分配,如下:
gcc -fmem-report -o test test.cpp

42) -march: 指定目标架构选项,如下
gcc -march=native -Q --help=target | grep march

43)-fpic或-fPIC: 如果支持目标机,编译器就生成位置无关目标码,适
用于共享库,如下
gcc -fpic -o test test.cpp

44)-fno-rtti: 禁用运行时类型信息,如下
gcc -fno-rtti -o test test.cpp

45)-fno-exceptions: 禁用异常机制,如下
gcc -fno-exceptions -o test test.cpp

46)–version 显示gcc版本号和版本信息

47)-mavx2 参数
如果使用AVX指令集

48)-std==c++0x 或 -std=c++11
旧的 -std=c++0x 仅适用于不支持 -std=c++11 的旧编译器版本,他们选择该名称是为了表达当时即
将推出的功能(和 ABI)的初步和不稳定性质C++11(当时还不清楚最终会变成 C++10 还是 
C++12)。他们更改了一些细节以适应 C++11 标准正式发布之前不断变化的标准工作草案。
(如果您的编译器支持 -std=c++11,则没有理由使用 -std=c++0x)

49)-Wpointer-arith
对函数指针或者void *类型的指针进行算术操作时给出警告。也很有用。 -Wall 并不会打开此项。

50)-Warray-bounds
启用数组边界检查,因此它可以帮助检查在编译期间数组下标是否超出范围

51)-msse4.2
cmake_cxx_compiler_version不是4.8的话,指令集就选择msse4.2,4.8的话就选择mavx2指令集
类似问题:x86使用的是crc32b和crc32q汇编指令完成CRC32C校验值计算功能,而ARM64平台使
用crc32cb、crc32ch、crc32cw、crc32cx 4个汇编指令完成CRC32C校验值计算功能。
(请使用crc32cb、crc32ch、crc32cw、crc32cx取代x86的CRC32系列汇编指令,替换方法如表所
示,并在编译时添加编译参数-mcpu=generic+crc。)

52)-fdiagnostics-color=auto
(如果编译一个项目错误警告太多,非常不好找,所以非常希望输出信息可以带有颜色)
如果你的 gcc 版本 >= 4.9.0,可以直接使用 -fdiagnostics-color=auto 参数,显示效果应该比 color-compile 好

53)add_definitions(-DUSING_AVX)  (cmake的C++编译器4.8才能用)
英特尔处理器(例如 SandyBridge 和 IvyBridge)已经整合了一个称为高级矢量扩展或 AVX 的指令
集。SIMD 指令范围的这一新增功能使 CPU 在处理大量浮点数据时速度更快。

更多编译选项

  • 补充mavx2
    本文面对对SSE等SIMD指令集有一定基础的读者,以单精度浮点数组求和为例演示了如何跨平台使用SSE、AVX指令集。因使用了stdint、zintrin、ccpuid这三个模块,可以完全避免手工编写汇编代码,具有很高可移植性。支持vc、gcc编译器,在Windows、Linux、Mac这三大平台上成功运行。
    传送门
  • -std=c++0x与-std=c++11的渊源
    上一个版本的C++国际标准是2003年发布的,所以叫C++ 03。
    然后C++国际标准委员会在研究C++ 03的下一个版本的时候,一开始计划是07年发布,所以最初这个标准叫C++ 07。
    但是到06年的时候,官方觉得07年肯定完不成C++ 07,而且官方觉得08年可能也完不成。最后干脆叫C++ 0x。x的意思是不知道到底能在07还是08还是09年完成。
    结果2010年的时候也没完成,最后在2011年终于完成了C++标准。所以最终定名为C++11。
    解释传送门

2)UNIX

  • debug

  • release

3)MSVC

六、gcc的碎碎念

①uint8_t的后缀_t的意思到底表示什么?
它就是一个结构的标注,可以理解为type/typedef的缩写,表示它是通过typedef定义的,而不是其它数据类型。uint8_t,uint16_t,uint32_t等都不是什么新的数据类型,它们只是使用typedef给类型起的别名,新瓶装老酒的把戏。不 过,不要小看了typedef,它对于你代码的维护会有很好的作用。比如C中没有bool,于是在一个软件中,一些程序员使用int,一些程序员使用 short,会比较混乱,最好就是用一个typedef来定义,如:

typedef char bool;

②c++1z就是c++17
-std=c++11,支持C++11标准;
-std=gnu++11,支持C++11标准和GNU扩展特性;

③GCC编译版本指令

GCC v4.8 不支持 C++17。
GCC v4.9 不支持 C++17
C++17 is supported by GCC v5, but you need -std=c++1z
C++17 is supported by GCC v8 by means of -std=c++17 (and by -std=c++1z which is deprecated 
since then).

④glibc 和 libc区别

相同点:

1)glibc 和 libc 都是 Linux 下的 C 函数库。

不同点

1)libc 是 Linux 下的 ANSI C 函数库;glibc 是 Linux 下的 GUN C 函数库。 
2) ANSI C 函数库是基本的 C 语言函数库,包含了 C 语言最基本的库函数。这个库可以根据头文件
划分为 15 个部分,其中包括: 
<ctype.h>:包含用来测试某个特征字符的函数的函数原型,以及用来转换大小写字母的函数原型;
<errno.h>:定义用来报告错误条件的宏;
<float.h>:包含系统的浮点数大小限制;
<math.h>:包含数学库函数的函数原型;
<stddef.h>:包含执行某些计算 C 所用的常见的函数定义;
<stdio.h>:包含标准输入输出库函数的函数原型,以及他们所用的信息;
<stdlib.h>:包含数字转换到文本,以及文本转换到数字的函数原型,还有内存分配、随机数字以及其他实用函数的函数原型;
<string.h>:包含字符串处理函数的函数原型;
<time.h>:包含时间和日期操作的函数原型和类型;
<stdarg.h>:包含函数原型和宏,用于处理未知数值和类型的函数的参数列表;
<signal.h>:包含函数原型和宏,用于处理程序执行期间可能出现的各种条件;
<setjmp.h>:包含可以绕过一般函数调用并返回序列的函数的原型,即非局部跳转;
<locale.h>:包含函数原型和其他信息,使程序可以针对所运行的地区进行修改。
地区的表示方法可以使计算机系统处理不同的数据表达约定,如全世界的日期、时间、美元数和大数字;
<assert.h>:包含宏和信息,用于进行诊断,帮助程序调试。
3)GNU C 函数库是一种类似于第三方插件的东西。由于 Linux 是用 C 语言写的,所以 Linux 的一
些操作是用 C 语言实现的,因此,GUN 组织开发了一个 C 语言的库 以便让我们更好的利用 C 语
言开发基于 Linux 操作系统的程序。不过现在的不同的 Linux 的发行版本对这两个函数库有不同的
处理方法,有的可能已经集成在同一个库里了。 
  • 总结
    glibc本身是GNU旗下的C标准库,后来逐渐成为了Linux的标准c库,而Linux下原来的标准c库Linux libc逐渐不再被维护。Linux下面的标准c库不仅有这一个,如uclibc(https://www.uclibc.org/)、klibc,以及上面被提到的Linux libc,但是glibc无疑是用得最多的。glibc在/lib目录下的.so文件为libc.so.6。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值