gcc 的常用编译选项
gcc 的参数组合比较随意,比如-Wall
其实就是-W all
,有时候空格等号和粘连的写法都是等价的,但像-O1
就不能分开写。
-E
预处理
-v
打印输出信息(默认 quiet)
-
从 stdio 中读取输入
多文件编译和第三方库
用 makefile 举例。先生成目标文件(.o
)再链接成可执行文件,相较于直接使用两个源文件编译成可执行文件,能够避免未修改的文件也要重新编译的开销。
main: main.o a.o gcc $^ -o $@%.o: %.c gcc -c $^ -o $@
makefile 能够简化成上面这么写,而不需要展开写所有的文件。$^
就代表上面所有的依赖,$@
指的就是冒号前面这个要输出的文件,%
是个模式的匹配。
更好地,还可以提取出所有的变量。
CC = gccSRC = main.c a.cOBJ = $(SRC:.c=.o)
main: $(OBJ) $(CC) $^ -o $@%.o: %.c $(CC) -c $^ -o $@
有时,文件目录嵌套了非常多层,要包含头文件时就需要写很多次的".."
,既不简洁也不优雅。gcc 中可以使用-I
这个编译选项来指定 include 目录,而不需要相对引用。
CC = gccSRC = main.c a.cOBJ = $(SRC:.c=.o)INCLUDE_PATH = -Ifolder/ -Iinclude1 -I include2
main: $(OBJ) $(CC) $^ -o $@%.o: %.c $(CC) -c $(INCLUDE_PATH) $^ -o $@
使用第三方库的时候,在 Linux 上apt
安装后只需要加入几个-l
选项就可以使用了。如果说修改了源码,想要用修改后的自己的库,那就要自己引入INCLUDE_PATH
和LIBRARY_PATH
,通常情况下程序会优先使用动态链接库(.a
或.la
结尾)以减小体积。apt
安装库时,把可执行文件放在/usr/bin
,头文件放在/usr/include
,库文件放在/usr/lib
,gcc 就能默认从这几个路径下搜索包含。
CC = gccSRC = main.c a.cOBJ = $(SRC:.c=.o)INCLUDE_PATH =LDFLAGS = -lgvs -lcgraph -lcdt
main: $(OBJ) $(CC) $^ $(LDFLAGS) -o $@%.o: %.c $(CC) -c $(INCLUDE_PATH) $^ -o $@
pkg-config 是一个能根据 package 自动补全 include 和 library 路径的工具,这样只需要提供 CFLAGS 和 LDFLAGS 就可以了。下面使用的``` 在 shell 中会先执行,然后用返回的值替代。
CC = gccSRC = main.c a.cOBJ = $(SRC:.c=.o)
CFLAGS = `pkg-config libgvc --cflags`# -I/usr/include/graphvizLDFLAGS = `pkg-config libgvc --libs`# -lgvs -lcgraph -lcdt
main: $(OBJ) $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@%.o: %.c $(CC) -c $(CFLAGS) $^ -o $@
跨平台和交叉编译
编译出来的文件会有一个可执行的configure
文件,这和 gnu 的构建三件套 GNU build system(automake,autoconf 和 libtools)有关。主要目的就是简化跨平台软件的构建过程。但最好用 cmake 来构建跨平台应用。
API 是应用程序接口,ABI 指的是应用程序二进制接口,和硬件、操作系统、编译器等等有关系。
出现一个新架构之后,如何将已有的编译器引入新架构?使用加拿大编译。
3 阶段实现自举。