编译过程
1、预处理阶段
gcc *.c -E -o *.i(预编译)
2、编译阶段
gcc *.i -S -o *.S(检查语法、翻译成汇编语言)
3、汇编阶段
gcc *.S -c -o *.o(汇编语言翻译成机器指令,不带地址的二进制文件)
4、链接阶段
gcc *.o -o *.elf(将机器码链接成段,添加各种地址信息到headinfo段中,带地址的二进制文件)
静态库和动态库的创建
1、静态库的创建
(1)创建.c文件,存放函数定义
sub.c
1 #include"sub.h"
2 int sub_func(int x,int y)
3 {
4 return x-y;
5 }
add.c
1 #include"add.h"
2 #include<stdarg.h>
3 int add_func(int x,int y)
4 {
5 return x+y;
6 }
(2)创建.h文件,存放函数声明
sub.h
#ifndef __SUB_FUNC_H
#define __SUB_FUNC_h
int sub_func(int x,int y);
#endif
add.h
#ifndef __ADD_FUNC_H
#define __ADD_FUNC_H
int add_func(int x,int y);
#endif
(3)将.c编译成.o
gcc -c add.c sub.c
(4)链接成.a
ar -crv libmylib.a add.o sub.o
(5)使用静态库
写main函数,然后使用静态库链接编译
gcc main.c -o main libmylib.a
./main
2、动态库的创建
(1)编写好代码之后直接链接
gcc -fpic -shared libmylib.so sub.c add.c
(2)编写好main函数链接动态库(动态库要移动到/lib,不然可执行文件执行不了或添加为所在目录添加环境变量)
gcc main.c -o main libmylib.so
还可以链接指定目录下的库(动态静态都可以)
gcc main.c -o main -L directory -l filename
3、添加环境变量(.so所在目录)
(1)永久添加
修改~/.bashrc文件,在末尾添加(也可以修改/etc/profile,这个是对所有用户生效的,bashrc只是对当前用户)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:目录名
保存并退出,输入 source ~/.bashrc
(2)临时添加(重启后失效)
#.表示当前目录,":"是分隔作用
export LD_LIBRARY_PATH=.:/opt/test
高质量编程之make管理器(使用makefile文件)
1、安装make
apt-get install build-essential
2、编写*.c、*.h文件
声明放在头文件、定义放在源文件
3、编写makefile文件
vim makefile
然后写入命令
例如:
main:main.o sub.o add.o
gcc main.o sub.o add.o -o main
main.o:main.c
gcc -c main.c -o main.o
sub.o:sub.c
gcc -c sub.c -o sub.o
add.o:add.c
gcc -c add.c -o add.o
clean:
rm -rf ./*.o
调用:
直接make(只会执行第一个目标,可以把后面所有目标写到第一个目标的依赖里面),就会编译出main的可执行文件(还会编译出main.o sub.o add.o)
make clean:就会删除掉所有.o文件(中间文件,可删除,但重新make会影响效率)
基本语法就是 目标:依赖文件
执行编译行为
还可以将目标、依赖文件,编译行为都改成变量(提高可变性)
将上面例子改写:
#TARGET、OBJS、CC均为make自有宏定义
#TARGET为目标、OBJS为依赖文件、CC为C语言编译器名字、CXX为C++编译器名字、RM为删除文件命令。默认命令是“rm –f”。
TARGET=main
#makefile默认换行就是另一个语句,如果一定要换行的话可以用\接上
OBJS+=main.o
OBJS+=add.o sub.o
CC=gcc
#变量名要用$(变量),$@表示目标文件、$<表示第一个依赖文件、$^表示所有依赖文件,这三个都是自动化变量
#静态规则
%.o:%.c
CC -c $< -o $@
$(TARGET):$(OBJS)
CC $^ -o $@
#创建伪目标,当目录下有clean文件时也能执行
.PHONY:clean
clean:
rm -rf ./*.o
@echo clean--- # @表示隐藏此语句,执行时不打印该语句直接执行
4、makefile多目录编程
首先,把所有头文件都放进include目录里,把.c文件放到source目录里(除了main.c,main.c直接放在当前目录,include和source是二级目录)
makefile如下:
CC=gcc
RM=rm -rf
#文件目录说明
#绝对不可以使用DIR=`pwd` #调用shell命令pwd获取当前目录
#详情查看文末链接调用shell命令的两种方式的差异
DIR :=$(shell pwd)
INCDIR :=$(DIR)/include
SRCDIR :=$(DIR)/source
#预编译选项
CPPFLAGS:= -I$(INCDIR)
TARGET=main
#获得依赖文件.o,所先用wildcard获取匹配模式文件名函数找到所有.c文件
#然后使用patsubst %.c,%.o,$(SRCS)模式字符串替换函数将SRCS中的.c全部替换成.c等到依赖文件(因为.o可能还没生成,只能替换得到)
#foreach dir,$(SRCDIR),$(wildcard $(dir)/*.c) 将$(SRCDIR)的字符串逐个拿出放到dir中,然后执行wildcard
SRCS:=$(wildcard *.c) #main.c
SRCS+=$(foreach dir,$(SRCDIR),$(wildcard $(dir)/*.c))
OBJS:=$(patsubst %.c,%.o,$(SRCS))
如果直接make不输入参数,则默认执行第一个目标所以在第一个目标依赖所有目标
all:$(TARGET) test clean
@echo ----
%.o:%.c
$(CC) -c $< %@ $(CPPFLAGS)
$(TARGET):$(OBJS)
$(CC) $^ -o $@ $(CPPFLAGS)
.PHONY:test clean
test:
@echo $(OBJS)
clean:
$(RM) $(OBJS)
5、其他
(1)=和:=的差别
a=3
b=$a
c:=$a #立即=a
a=5 #要使用该变量时再=a,如果此时a已经改变,就是改变后的值了
test:
echo $(b) #b=5
echo $(c) #c=3
(2)$< $^ $@ $?
$<:第一个依赖;
$^:全部依赖
$@:目标
$?:被修改过的依赖(时间戳比目标文件晚的依赖,因为目标文件最后生成,如果依赖的时间戳比目标文件晚,则代表被修改过)
(3)调用shell命令的两种方式(获取目录时的大坑)
shell命令的两种方式的差异
直接引用 `shell命令` #末尾是换行符,makefile换行是另一个语句了,所以不能用这个方式获取依赖文件的目录
函数调用$(shell shell命令) #换行符会被替换成空格
$(shell echo *.c) 作用和$(wildcard *.c)一样
(4)路径的指定
头文件路径的搜索顺序:
由参数-I指定的路径(用CPPFLAGS:= -I$(CPATH)存储,指定多个路径时,按指定路径顺序查找)
然后再找gcc的环境变量C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH
再找内定目录
动态库的搜索路径搜索的先后顺序是:
编译目标代码时指定的动态库搜索路径;
环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
配置文件/etc/ld.so.conf中指定的动态库搜索路径(配置直接添加目录即可,配置完要用ldconfig);
默认的动态库搜索路径/lib;
默认的动态库搜索路径/usr/lib。
(5)gcc参数
-c 用于把源码文件编译成 .o 对象文件,不进行链接过程
-o 用于连接生成可执行文件,在其后可以指定输出文件的名称
-g 用于在生成的目标可执行文件中,添加调试信息,可以使用GDB进行调试
-Idir 用于把新目录添加到include路径上,可以使用相对和绝对路径,“-I.”、“-I./include”、“-I/opt/include”
-Ldir 用于把新目录添加到库搜索路径上,可以使用相对和绝对路径,“-L.”、“-L./include”、“-L/opt/include”
-Wall 生成常见的所有告警信息,且停止编译,具体是哪些告警信息,请参见GCC手册,一般用这个足矣!
-w 关闭所有告警信息
-fPIC 用于生成位置无关的代码
可以写在CPPFLAGS(预编译,一般存储头文件目录,-I$(INCDIR))、CFLAGS(编译)、LBFLAGS(链接库,存储库目录,-L$(LBDIR))
(6)条件判断
判断是否相等(ifeq,ifneq;ifeq相等时为true,ifneq为false)
ifeq ($(num),11) #判断num是不是等于11
echo "num=$(num)"
else ifeq($(num),12)
echo "num=12"
else
echo "nothing"
endif
判断是否定义(ifdef和ifndef)
ifdef $(num)
echo "yes"
else
echo "no"
endif