Convention
compilation: 从源代码到机器码的所有过程的总称include path: 头文件搜索路径liarary search path(或者link path): 库文件搜索路径
Compiling a C program
警告相关
-Wall: 开启所有常用的编译警告, __推荐任何情况下都加上该option__。当有警告信息输出时,信息的格式为file:row-number:column-number:message
Compiling files independently
In general, linking is faster than compilation—in a large project with many source files, recompiling only those that have been modified can make a significant saving. The process of recompiling only the modified files in a project can be automated using GNU
Make
- 意义: 避免了每一次都从头开始进行全部的编译
object file: 也是机器码,只不过任何其他文件中的function或者variable的地址都被留空(left undefined)-c: 为某个源文件生成object file, 例如gcc -Wall -c main.c会生成main.o
Linking with external libraries
/tmp/xxx.o ... undefined reference: 出现了链接错误,这里的/tmp/xxx.o就是编译器自动生成的临时object file。(xxx.o这个名字是我自己瞎取的)-lNAME: 让链接器尝试从静态库libNAME.a中获取有用的符号
Compilation options
Setting search paths
Note that you should never place the absolute paths of header files in #include statements in your source code, as this will prevent the program from compiling on other systems.
-I: 增加include path, 实际上就是影响了INCLUDE_PATH这个变量的值-L: 增加library search path- environment variable: 也可以通过在shell中修改
C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,LIBRARY_PATH这几个变量去增添搜索路径 - 同时指定多个路径:
- command line: 例如
-I. -I/opt/gdbm-1.8.3/include -I/net/include - environment variable: 例如
C_INCLUDE_PATH=.:/opt/gdbm-1.8.3/include:/net/include, 用:进行分隔 - 其实,如果不想写了完整的CMakeLists.txt之后再用
compile_commands.json实现vim的跳转的话,可以事先配置一个环境变量CPLUS_INCLUDE_PATH,加入需要搜索的路径,然后export CPLUS_INCLUDE_PATH,vim(clangd)就知道需要在哪里搜索头文件了。
- command line: 例如
- 路径的搜索顺序:先搜索通过
-I或-L指定的路径;再搜索通过environment variable指定的路径;再搜索默认路径;
Shared libraries and static libraries
因为链接一个动态库可以使得可执行文件的体积更小,并且对动态库进行版本更新不会影响到使用它的可执行文件(只要接口不变),所以,只要有可能,GCC就会默认使用动态链接
- default: 例如,如果GCC同时搜索到了
libgdbm.a和libgdbm.so,而编译时使用的是-lgdbm,那么GCC默认链接的是libgdbm.so - 增添动态库搜索路径:1. 设置环境变量
LD_LIBRARY_PATH -static: 强制GCC仅使用静态链接
C language standards
By default, gcc compiles programs using the GNU dialect of the C language, referred to as GNU C. This dialect incorporates the official ANSI/ISO standard for the C language with several useful GNU extensions, such as nested functions and variable-size arrays.
For reference, the non-standard keywords and macros defined by the GNU C extensions are asm, inline, typeof, unix and vax
-ansi: 禁用掉GNU C中对于ANSI/ISO C标准的扩展,避免一些ANSI/ISO标准的C程序在GCC上进行编译时可能出现的不兼容的情况- 注意:
-ansi其实就是-std=c89的同义词
- 注意:
-pedantic: 当和-ansi结合的时候,会严格执行ANSI/ISO标准,禁用掉任何GNU C的扩展(真的非常严格)- 举个例子,GNU C的变长数组,实际上是对ANSI/ISO的后向兼容扩展,其他的标准C程序不会因为GNU C提供了变长数组的支持就无法在GNU C下编译。所以,即使加上了
-ansi,照样是能够编译的;但如果再加上了-pedantic,编译器就会迂腐的照搬标准,程序就编译不了了 - 作为对比,类似于
asm这样的变量,在GNU C下是预先定义好了的,那么这样的程序,虽然在ANSI/ISO中可以编译,但是在GNU C中反而就编译不了了,无法后向兼容。
- 举个例子,GNU C的变长数组,实际上是对ANSI/ISO的后向兼容扩展,其他的标准C程序不会因为GNU C提供了变长数组的支持就无法在GNU C下编译。所以,即使加上了
Additional warning options
-W: 通常和-Wall一起使用,报告一些常见的潜在问题。(比如判断一个无符号数的正负性)-Wconversion: 隐式转换可能出现的问题-Wshadow: 定义了和外部空间同名的局部变量-Wcast-qual: 强制去除了const这样的修饰符(qualifier)-Werror: 强制编译器,只要遇到warn,就将其转换为error,使得编译终止
Using the preprocessor
Some macros are automatically defined by the compiler—these typically use a reserved namespace beginning with a double-underscore prefix ‘__’.
-DName: 利用command line定义一个宏,宏名是Name, 值为1-DName="2+4": 利用command line定义一个宏,宏名是Name, 值为2+4-DName="": 利用command line定义一个宏,宏名是Name, 值为空,但依旧已被定义cpp -dM /dev/null: 展示GCC预定义了哪些macro。/dev/null的意思是分析这个空文件,因此只输出GCC预定义的macro-E: 在stdout中输出经过预处理器处理之后的文件内容. (The preprocessor also inserts lines recording the source file and line numbers in the form # line-number “source-file”, to aid in debugging and allow the compiler to issue error messages referring to this information.)-save-temps: 保留预处理后的xxx.i(或者xxx.ii)源文件,编译后的xxx.s汇编代码文件,汇编之后的xxx.o可重定位目标文件。i是C代码,ii是C++代码
Compiling for debugging
The debug option(
-g) works by storing the names of functions and variables (and all the references to them), with their corresponding source code line-numbers, in a symbol table in object files and executables.
本章仅对gdb进行了简单的阐述,详细的学习,需要阅读:《Debugging with GDB: The GNU Source-Level Debugger》
$ ulimit -c: 显示系统当前允许产生的core文件的最大大小$ ulimit -c unlimited: 设置core文件大小无限制- 设置完毕后,后续执行程序时,如果崩掉,就会生成core文件
gdb EXECUTABLE-FILE CORE-FILE: 开始调试。(可执行文件和core文件都必须提供)
1 |
|
1 | # 例子 |
Compiling with optimization
Source-level optimization
- common subexpression elimination: 利用临时变量存储一些中间值,从而减少运算
- function inlining
Optimization level
optimizations may not necessarily make a program faster in every case.
从0到3:
-O0: 默认级别。编译器不执行任何优化,也不进行指令重排。适合于debug-O1: 执行一些常用的优化手段,但没有speed-space tradeoff以及指令重排-O2: 除了O1的手段之后,还加入了指令重排。此外,该级别是最时候发布的级别-O3: 编译时间最久。可能会增加最终的可执行文件的体积-Os: 生成体积最小的可执行文件
Compiling a C++ program
永远遵守一个规定:C++代码使用g++,而C代码使用gcc
explicit template instantiation
-fno-implicit-templates: 默认情况下,在编译过程中,每当使用到了一个模板,编译器对其进行隐式的具现化,而该option的作用就是禁止这个行为。程序中所有需要使用的模板具现化类型,会在另一个显示具现化文件中给出。这样做的好处是,所有模板的具现化类型都只有一个存在
Platform-specific options
Code produced with a specific ‘-march=CPU’ option will be faster but will not run on other processors in the x86 family.
以-m开头的option都是和平台特定的选项
-march=: 指定编译为哪一种特定的处理器指令格式。会更快,但是其他处理器型号就运行不了了-m32: 生成32位程序(默认是64位)
Compiler-related tools
gcov和gprof不想看了,先放一放
Creating a library with the GNU archiver
ar cr <library-name> <object-file1 object-file2 ...>: 生成静态库文件,cr的意思是create and replacear t <library-name>: 展示一个静态库中包含有哪些object file
Examining compiled flags
file: 检测executable file或者object file的一些flag,比如链接类型,LSB还是MSB,在哪类平台上进行编译的,等等;nm: 获取可执行文件或者object file的符号表- 当第2列为”T”时,说明对应的函数是已经被定义了的;反之如果是”U”,则说明没有定义
- A complete explanation of the output of nm can be found in the GNU Binutils manual.
ldd: 明确可执行文件需要依赖于哪些动态库
1 | # nm 例子 |
1 | # 例子 |
Further reading
The definitive guide to GCC is the official reference manual, “Using GCC”, published by GNU Press: