二、关于编译和链接
把大量的 Object File 合成执行文件,这个动作叫作链接(link);链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在 Windows 下这种包叫“库文件”(Library File),也就是 .lib 文件,在 UNIX下,是 Archive File,也就是 .a 文件。
静态库概念:转载https://www.cnblogs.com/zzdbullet/p/10150323.html
1.库是预编译的目标文件(object files)的集合,它们可以被链接进程序。静态库以后缀为”.a”的特殊的存档(archive file)存储。
2.标准系统库可在目录/usr/lib与/lib中找到。比如,在类Unix系统中C语言的数序库一般存储为文件/usr/lib/libm.a。该库中函数的原型声明在头文件/usr/include/math.h中。
3.C标准库本身存储为/usr/lib/libc.a,它包含ANS1/ISO标准指定的函数,比如printf。对每一个C程序来说,libc.a都默认被链接。
在tiger.c程序中调用一个数学库libm.a中sin函数。系统默认链接的是libc.a但是没有默认链接libm.a
1>函数sin(),未在本程序中定义也不在默认库’libc.a’中,除非被指定,编译器也不会链接’libm.a’。
2>为使编译器能将sin()链接进主程序‘test.c’,需要提供数学库’libm.a’.。
3>使用方法:gcc tiger.c /usr/lib/libm.a -o tiger 或者libm.so gcc tiger.c /usr/lib/libm.so -o tiger
则可以编译通过。为了避免在命令行中指定长的路径,编译器为链接函数库提供了快捷的选项“-l”。因此可以使用下面的方法:
gcc tiger.c -lm –o tiger
注:选项-lNAME使用连接器尝试链接系统库目录中的函数库文件libNAME.a。
#include<stdio.h>
#include<math.h>
int main()
{
double x = 2.0;
double y = sin(x);
printf(“the result:%f\n”,y);
return 0;
}
二.生成和使用静态库
1.静态库是obj文件的一个集合,通常静态库以”.a”为后缀。静态库由程序ar生成。
2.静态库的优点是可以在不用重新编译程序库代码的情况下,进行程序的重新链接,这种方法节省了编译过程的时间(在编译大型程序的时候,需要花费很长的时间)。静态库的另一个优点是开发者可以提供库文件给使用的人员,不用开放源代码,这是库函数提供者经常采用的手段。
3.通过一个实例来了解如何自己生成静态库和使用静态库
首先生成静态库
1>在test文件夹下有三个文件:main.c ,tiger.c,tiger.h;
a. main.c文件中的内容:
#include<stdio.h>
#include”tiger.h” //注意include该文件,并不是仅链入.a文件即可
int main(void)
{
printf(“sum=%d\n”,add(3,5));
return 0;
}
b.tiger.h文件中的内容:
#ifndef __TIGER__
#define __TIGER__int add(int a,int b);
#endif
c.tiger.c文件中的内容
int add(int a,int b) { return a+b; }
2>创建静态库的最基本步骤是生成目标文件tiger.o
gcc -o tiger.o -c tiger.c
3>然后生成静态库libadd.a:
ar -rcs libadd.a tiger.o
其次使用静态库
1>使用gcc命令带上库文件就OK了
gcc -o main main.c libadd.a
2> 也可以使用命令”-l库名”进行,库名是不包含库函数库和扩展名的字符串。
gcc -o main main.c -ladd
上面的命令执行完后,系统返回:
Cannot find –ladd
说明:上面的命令将在系统默认的路径查找add函数库,并把他链接到要生成的目标程序上。系统提示没有找到库文件add,这是由于add库函数没有在系统默认的查找路径下,我们需要认为指定库函数的路径,例如:库文件和当前编译文件在同一目录下: gcc -o main main.c -L ./ -ladd
系统就能正常生成可执行文件。
说明:在使用-l选项时,-o选项的目的名要在-l链接的库名之前,否则gcc会认为-l是生成的目标而出错。
三、makefile
可执行目标:edit
源文件:8个c文件,3个h文件:main.c defs.h kbd.c command.c command.h utils.c display.c insert.c buffer.h search.c files.c
依赖关系:
edit:main.o kbd.o command.o utils.o display.o insert.o search.o files.o
gcc -o edit main.o kbd.o command.o display.o insert.o search.o file s.o utils.o
main.o: main.c defs.h
gcc -c main.c
kbd.o: kbd.c defs.h
gcc -c kbd.c
command.o: command.c defs.h command.h
gcc -c command.c
display.o: display.c defs.h buffer.h
gcc -c display.c
insert.o: insert.c defs.h buffer.h
gcc -c insert.c
search.o: search.c defs.h buffer.h
gcc -c search.c
files.o:files.c defs.h buffer.h command.h
gcc -c files.c
2、使用变量的makeflie
edit:$(OBJS)
gcc -o edit $(OBJS)
main.o: main.c defs.h
gcc -c main.c
kbd.o: kbd.c defs.h
gcc -c kbd.c
command.o: command.c defs.h command.h
gcc -c command.c
display.o: display.c defs.h buffer.h
gcc -c display.c
insert.o: insert.c defs.h buffer.h
gcc -c insert.c
search.o: search.c defs.h buffer.h
gcc -c search.c
files.o:files.c defs.h buffer.h command.h
gcc -c files.c
3、让makefile自动推导(如何优化.o依赖的.c文件,以及gcc编译.c文件)
OBJS = main.o kbd.o command.o utils.o display.o insert.o search.o files.o
四、在习题中学习
(1)同一目录中根据5个文件编写makefile
//main.c
#include<stdio.h>
//#include"visit.h"
//#include"study.h"int main(void)
{
printf("main.c");
visit();
study();
return 0;
}//visit.c
#include<stdio.h>
void visit()
{
printf("visit friend today\n");
}//study.c
#include<stdio.h>
void study()
{
printf("study embedded system today\n");
}//visit.h
void visit();//study.h
void study();
如何写makefile呢??很多方式
##########################################
#第一种方式
#a.out:main.o visit.o study.o
# gcc -o a.out main.o visit.o study.o
#
#main.o:main.c visit.h study.h
# gcc -c main.c -o main.o
#visit.o:visit.c visit.h
# gcc -c visit.c -o visit.o
#study.o:study.c study.h
# gcc -c study.c -o study.o
#
#clean:
# rm -rf a.out main.o visit.o study.o
###############################################
#########################################
#第二种方式:不知多少为何,这种方式就是出错误,隐含规则好像有问题
TARGET = a.out
OBJ = study.o main.o visit.o$(TARGET):$(OBJ)
gcc -o $(TARGET) $(OBJ)
visit.o:visit.h
study.o:study.h
clean:
rm -rf $(TARGET) $(OBJ)
##############################################
######################################### #第三种方式 #include test1.mk #extern TARGET TARGET=A.OUT OBJ = study.o main.o visit.o SRCS= main.c study.c visit.c $(TARGET):$(OBJ) gcc $^ -o $@ visit.o:visit.h study.o:study.h %.o:%.c 理论上这两句话是不需要写的,因为隐含规则会自动区编译,可以通过make -p查看隐含规则 gcc $< -c clean: rm -rf $(TARGET) $(OBJ) ##############################################
#第第四种方式:引用外部makefile:include test1.mk
(2)如果不在同一目录中,又该如何处理呢?
目录结构
|
|__->main.c
|__head
| |__->visit.h study.h
|__->study.c
|__->visit.c
|__->Makefile
//Makefile
TGT = a.out
SRCS = main.c test.c foo.c
指定交叉编译环境
#CROSS_COMPILER = arm-linux-
CC = $(CROSS_COMPILER)gcc
CFLAGS=-I$(HEAD_DIR) //编译选项
CUR_DIR = $(shell pwd)
HEAD_DIR = $(CUR_DIR)/head
$(TGT):$(SRCS:.c=.o)
$(CC) $^ $(CFLAGS)-o $@%.o:%.c
$(CC) $< -c
#下面的规则用来说明,头文件的更新,应该使得依赖于它的文件被重新生成
%.d:%c
$(CC)n -MM $< >$@
#下面的一行用来告诉make,将上面的模式规则中的命令执行结果包含进当前文件(下面三行作用一样)
#include $(SRCS:.c=.o)
#-include $(SRCS:.c=.o)
sinclude $(SRCS:.c=.o).PHONY:clean:
rm -f $(TGT) $(SRCS:.c=.o) $(SRCS:.c=.d)
(3)如果按照下面的目录结构存放文件,请改写Makefile文件。
bin:存放生成的可执行文件
obj:存放.o文件
include:存放visit.h、study.h
src:存放main.c、visit.c、study.c和Makefile.
目录结构
|__src
| |__->main.c visit.c study.c
|__include
| |__->visit.h study.h
|__obj
|__bin
|__->Makefile