概念
编译:把源代码翻译成机器语言的目标文件
链接:把众多目标文件链接成可执行文件
编译预处理
gcc -E -o test.i test.c
对伪指令(或预处理指令,以#号开头的代码行)和特殊符号进行处理
- 宏定义,#define
- 条件编译指令,#ifdef,#ifndef,#endif,#if,#else,#elif,#endif
- 头文件,#include,预处理将头文件的定义加入到输出文件中!!!!只有这一步需要头文件!!!
- 特殊符号,__FILE__,__LINE__,__DATE__,__STDC__,__TIME__
https://blog.youkuaiyun.com/kz_java/article/details/104632270
https://www.cnblogs.com/rusty/archive/2011/03/27/1996806.html
编译
gcc -S -o test.s test.c
通过词法分析和语法分析,在确认所有的指令都符合语法规则后,翻译成汇编代码。
这一步是最耗时间的。
以及相关优化
https://www.cnblogs.com/wangsens/p/7612803.html
https://blog.youkuaiyun.com/qq100440110/article/details/51289786
https://www.cnblogs.com/plus666/p/14135246.html
汇编
gcc -c -o test.o test.c
把汇编语言翻译成机器指令的目标文件
链接
gcc -o test test.o
把有关目标文件链接成可执行文件
除了将多个.o文件链接这种方式外,还有静态链接和动态链接。
- 静态链接
ar -rcs libtest.a test1.o test2.o test3.o
ar -t libtest.a #查看
- 动态链接
gcc -fpic -c -o test.o test.c
gcc -shared -o libtest.so test.o
区别:静态链接和动态链接最大区别在于链接的时机不一样,静态链接是形成在可执行程序前,而动态链接是在程序执行时。
- 动态库的搜索,用ldd查看动态库依赖关系(是否找不到动态库)
3.1 将动态库放入/lib路径中
3.2 将动态库路径加入/etc/ld.so.conf,调用命令 ldconfig 生效
3.3 将动态库路径加入到LD_LIBRARY_PATH环境变量中
动态链接的时候,动态库加载到Memory Mapping Segment
图片地址 http://static.duartes.org/img/blogPosts/linuxFlexibleAddressSpaceLayout.png
gcc的几个参数
- -I,指定头文件路径
- -l,库名
- -L,库路径
- -Wall,提示更多警告信息
- -On,n=0~3编译优化,n越大优化得越多
- -g,包含调试信息,配合gdb使用
- -D xx,向程序中“动态”注册宏定义,#ifdef xx
makefile
1个规则,2个函数,3个变量
规则
目标:依赖
命令
main: main.o
gcc -o main main.o
main.o:main.c
gcc -c -o main.o main.c
函数
wildcard 通配符,patsubst 替换
src = $(wildcard *.c) #将.c文件名组成列表,赋值给src
obj = $(patsubst %.c, %.o, $(src)) #将src变量中所有后缀为.c的文件替换为.o
#特殊路径
src = $(wildcard ./src/*.c)
obj = $(patsubst ./src/%.c, ./obj/%.o, $(src))
变量
$@: 表示规则中的目标(只能出现在命令的位置上)
$<:表示第一个依赖条件(只能出现在命令的位置上)
$^:表示所有依赖条件(只能出现在命令的位置上)
all: first second third #在当前路径创建这三个文件
@echo "\$$@ = $@"
@echo "$$< = $<"
@echo "$$^ = $^"
#$@ = all
#$< = first
#$^ = first second third
模式规则
%.o:%.c
gcc -c -o $@ $<
静态模式
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
伪对象(伪目标)
.PHONY,防止当前目录有对应文件,导致make失败,比如当前目录存在clean文件
.PHONY: clean ALL #表示clean, ALL是伪目标
特殊符号-
表示如果执行失败, 则忽略, 继续执行之后的操作
clean:
-rm $(obj)
=, :=, ?=, +=区别
https://www.cnblogs.com/zgq0/p/8716150.html
= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值
=和:=的区别
x = foo
y = $(x) bar #xyz bar
x = xyz
x := foo
y := $(x) bar #foo bar
x := xyz
http://c.biancheng.net/view/7051.html
vpath目标文件搜索
VPATH 和 vpath 的区别:VPATH 是变量,更具体的说是环境变量,Makefile 中的一种特殊变量,使用时需要指定文件的路径;vpath 是关键字,按照模式搜索,也可以说成是选择搜索。搜索的时候不仅需要加上文件的路径,还需要加上相应限制的条件。
VPATH
VPATH := src
我们可以这样理解,把 src 的值赋值给变量 VPATH,所以在执行 make 的时候会从 src 目录下找我们需要的文件。
当存在多个路径的时候我们可以这样写:
VPATH := src car
或者是
VPATH := src:car
使用 VPATH 的情况是前路径下的文件较少,或者是搜索的文件不能使用通配符表示,比如执行文件在bin目录下,这些情况下使用VPATH最好。
.SUFFIXES
# .
# ├── include
# │ ├── add.h
# │ └── sub.h
# ├── main
# ├── Makefile
# ├── obj
# │ ├── add.o
# │ ├── main.o
# │ └── sub.o
# └── src
# ├── add.c
# ├── main.c
# └── sub.c
CFLAGS := -O -Wall
INC := -I ./include
LIBS :=
BIN := main
#SRC := $(wildcard ./src/*.c)
SRC := main.c add.c sub.c
ODIR := obj
OBJS := $(patsubst %.c, $(ODIR)/%.o, $(SRC))
CC := gcc
all: $(BIN)
$(BIN): $(OBJS)
$(CC) -o $@ $^ $(LIBS)
$(OBJS): $(ODIR)
$(ODIR):
@mkdir -p $@
$(ODIR)/%.o:%.c
#$(OBJS): ./obj/%.o : %.c
$(CC) -c $(CFLAGS) -o $@ $< $(INC)
clean:
-rm $(OBJS) $(BIN)
.PHONY: all clean
#.SUFFIXES
vpath %.c src
vpath %.h include