Makefile库文件详解

Makefile库文件

前言

学习杜老师推荐的Makefile教程视频,链接。记录下个人学习笔记,仅供自己参考。

之前有转载过杜老师的从零Makefile落地算法大项目文章,感兴趣的可以看看。

本次主要学习Makefile编译库文件的相关内容。(from ChatGPT)

1.库文件

在实际的程序开发中,为了提高程序的复用性,会将一些常见的功能或算法封装成库文件,供其它程序调用。常见的库文件包括静态库动态库两种。本篇博文的目的是利用Makefile将add.cpp文件编译成库文件(静态库和动态库)并在main.cpp中使用

文件的总体目录如下:

include
   └─add.hpp
src
 ├─add.cpp
 └─main.cpp
Makefile

其中,include文件夹下存放着add.hpp头文件,src文件夹下存放着add.cppmain.cpp源文件。

add.hpp内容如下:

#ifndef ADD_HPP
#define ADD_HPP
int add(int a, int b);

#endif // ADD_HPP

add.cpp内容如下:

int add(int a, int b)
{
    return a+b;
}

main.cpp内容如下:

#include <stdio.h>
#include "add.hpp"

int main()
{
    int a=10; int b=5;
    int res = add(a, b);
    printf("a + b = %d\n", res);

    return 0;
}

2.静态库

2.1 定义

静态库是指编译时被链接到目标文件中的库文件,它包含了预编译的代码和数据。当程序运行时,操作系统会将静态库中的代码和数据直接复制到进程的地址空间中,供程序调用。静态库的优点是链接方便,不需要额外的运行时支持库,但缺点是程序的体积会增大,因为静态库中的代码和数据会被完整地复制到目标文件中。

静态库的创建可以使用ar命令,下面是一个创建静态库的示例:

$ g++ -c add.cpp sub.cpp
$ ar -r libmath.a add.o sub.o

静态库的使用可以在编译命令中加入-l-L选项。其中-l选项用于指定要链接的库名,-L选项用于指定库文件的搜索路径,下面是一个使用静态库的示例:

$ g++ main.cpp -lmath -L./

其中-lmath表示要链接的库名为libmath.a(注意在链接时的库名是去除lib和.a之后剩余的部分),-L./表示库文件在当前目录下。

2.2 创建静态库

示例如下:

add_src := src/add.cpp
add_obj := objs/add.o

compile_options := -g -O3 -w 

$(add_obj) : $(add_src)
	@mkdir -p $(dir $@)
	@g++ -c $^ -o $@ $(compile_options)

libadd.a : $(add_obj)
	@ar r $@ $^

staticlib : libadd.a
	@echo "static_library created successfully!"

debug :
	@echo $(compile_options)

clean :
	@rm -rf objs libadd.a

.PHONY : debug clean staticlib

具体而言,该Makefile文件的各个部分及其含义如下:

  • add_src := src/add.cpp:定义一个变量add_src,表示源文件路径。
  • add_obj := objs/add.o:定义一个变量add_obj,表示目标文件路径。
  • compile_options := -g -O3 -w:定义一个变量compile_options,表示编译选项,包括调试信息、优化等级和警告提示。
  • $(add_obj) : $(add_src):定义一个规则,表示将源文件编译成目标文件。
    • @mkdir -p $(dir $@):如果目录objs不存在,则创建该目录。
    • @g++ -c $^ -o $@ $(compile_options):将源文件编译成目标文件,并指定编译选项。
  • libadd.a : $(add_obj):定义一个规则,表示将目标文件打包成静态库。
    • @ar r $@ $^:将目标文件打包成静态库。
  • staticlib : libadd.a:定义一个规则,表示创建静态库。
    • @echo "static_library created successfully!":输出一条提示信息。
  • debug ::定义一个规则,表示输出编译选项。
    • @echo $(compile_options):输出编译选项。
  • clean ::定义一个规则,表示删除目标文件和静态库。
    • @rm -rf objs libadd.a:删除目标文件和静态库。
  • .PHONY : debug clean staticlib:声明规则debugcleanstaticlib为“伪目标”,表示不产生对应的文件,只是作为一个操作名称。

执行make staticlib运行效果如下:

ar: creating libadd.a
static_library created successfully!

2.3 使用静态库

示例如下:

add_src := src/add.cpp
add_obj := objs/add.o

include_paths := include
include_paths := $(patsubst %,-I%,$(include_paths))

compile_options := -g -O3 -w $(include_paths)

$(add_obj) : $(add_src)
	@mkdir -p $(dir $@)
	@g++ -c $^ -o $@ $(compile_options)

libadd.a : $(add_obj)
	@ar r $@ $^

staticlib : libadd.a
	@echo "static_library created successfully!"

workapce/pro : src/main.cpp libadd.a
	@mkdir -p $(dir $@)
	@g++ $< -o $@ -L./ -ladd $(compile_options)

run : workapce/pro
	@./$<

debug :
	@echo $(compile_options)

clean :
	@rm -rf objs libadd.a workapce/pro

.PHONY : debug clean static_library run

具体而言,workspace/pro目标依赖于src/main.cpp和之前生成的静态库libadd.a,当执行make workspace/pro命令时,将编译生成可执行文件pro-L./ -ladd指定链接器需要从当前目录下查找静态库文件,并使用静态库文件libadd.arun目标依赖于workspace/pro,当执行make run命令时,会执行可执行文件workspace/pro

执行make run运行效果如下:

ar: creating libadd.a
a + b = 15

3.动态库

3.1 定义

动态库是指程序运行时被加载地库文件,它不会被复制到目标文件中。当程序需要调用动态库中的函数时,操作系统会将动态库中的代码和数据加载到进程的地址空间中,供程序调用。相比于静态库,动态库的优点时程序体积较小,多个程序可以共享同一个动态库,但缺点时需要额外的运行时支持库,链接比较麻烦。静态库的扩展名通常为.so

动态库的创建可以使用-shared选项,下面是一个创建动态库的示例:

$ g++ -c add.cpp sub.cpp
$ g++ -shared -o libmath.so add.o sub.o

动态库的使用可以在编译命令中加入-l-L选项。和静态库不同的是,**动态库需要在运行时才能找到并加载,因此需要使用LD_LIBRARY_PATH环境变量指定库文件的搜索路径。**下面是一个使用动态库的示例:

$ export LD_LIBRARY_PATH=.
$ g++ main.cpp -lmath -L./ -Wl,-rpath=./

其中-lmath表示要链接的库名为libmath.so(注意在链接时的库名是去除lib和.so之后剩余的部分),-L./表示库文件在当前目录下。export LD_LIBRARY_PATH=.表示将当前目录添加到库文件的搜索路径中。

-Wl选项用于将后面的选项传递给链接器,-rpath选项用于指定运行时动态库的搜索路径。

3.2 创建动态库

示例如下:

add_src := src/add.cpp
add_obj := objs/add.o

compile_options := -g -O3 -w

$(add_obj) : $(add_src)
	@mkdir -p $(dir $@)
	@g++ -c $^ -o $@ $(compile_options) -fPIC

libadd.so : $(add_obj)
	@g++ -shared -fPIC -o $@ $^

sharedlib : libadd.so
	@echo "share_library created successfully!"

debug :
	@echo $(compile_options)

clean :
	@rm -rf objs libadd.so

.PHONY : debug clean sharedlib

具体而言,该Makefile文件的各个部分及其含义如下:

  • add_src := src/add.cpp:将要编译成动态库的源文件
  • add_obj := objs/add.o:将要编译成动态库的对象文件
  • compile_options := -g -O3 -w:编译选项,包括调试信息、优化等级和不显示警告
  • $(add_obj) : $(add_src):生成对象文件的规则,表示将add_src编译成add_obj
    • @mkdir -p $(dir $@):如果不存在对象文件所在目录,则创建该目录
    • @g++ -c $^ -o $@ $(compile_options) -fPIC:编译源文件生成对象文件,其中-fPIC选项表示编译为位置无关代码,是动态库所必需的选项
  • libadd.so : $(add_obj):生成动态库的规则,表示将add_obj链接为动态库libadd.so
    • @g++ -shared -fPIC -o $@ $^:链接对象文件生成动态库,其中-shared表示生成动态库,-fPIC选项同上
  • sharedlib : libadd.so:生成动态库的目标,表示生成动态库成功
  • debug ::输出编译选项
    • @echo $(compile_options):输出compile_options的值
  • clean ::清理目标文件和动态库
    • @rm -rf objs libadd.so:删除objs目录和libadd.so文件
  • .PHONY : debug clean sharedlib:声明debugcleansharedlib为伪目标,防止与同名文件或目录冲突

执行make sharedlib运行效果如下:

share_library created successfully!

3.3 使用动态库

示例如下:

add_src := src/add.cpp
add_obj := objs/add.o

include_paths := include
include_paths := $(patsubst %,-I%,$(include_paths))

compile_options := -g -O3 -w $(include_paths)

$(add_obj) : $(add_src)
	@mkdir -p $(dir $@)
	@g++ -c $^ -o $@ $(compile_options) -fPIC

libadd.so : $(add_obj)
	@g++ -shared -fPIC -o $@ $^

sharedlib : libadd.so
	@echo "share_library created successfully!"

workspace/pro : src/main.cpp libadd.so
	@mkdir -p $(dir $@)
	@g++ $< -o $@ -L./ -Wl,-rpath=./ -ladd $(compile_options)

run : workspace/pro
	@export LD_LIBRARY_PATH=./;
	@./$<

debug :
	@echo $(compile_options)

clean :
	@rm -rf objs libadd.so workspace/pro

.PHONY : debug clean sharedlib run

在此代码中,workspace/pro为生成的可执行文件,依赖于src/main.cpplibadd.so。使用-L选项指定动态库的搜索路径,使用-l选项指定动态库的名称。-Wl,-rpath=./选项指定动态库的搜索路径。另外,在运行时,还需要设置环境变量LD_LIBRARY_PATH,将动态库路径添加到搜索路径中。

执行make run运行效果如下:

a + b = 15

4.静态库 vs. 动态库

4.1 静态库

静态库(Static Library)是指在程序编译时被链接进可执行程序的库,与动态库不同的是,静态库在程序编译时就已经被链接,因此程序的运行时不需要进行库文件的加载。静态库的文件格式为.a(Linux).lib(Windows)

优点

  • 程序运行时速度快:由于库文件已经被链接到程序中,程序运行时无需进行库文件的加载;
  • 程序依赖性低:静态库在程序编译时已经被链接,因此程序的运行时不需要依赖相应的库文件;
  • 程序更加安全:由于静态库已经被编译进程序中,因此不容易被黑客攻击。

缺点

  • 可执行程序体积大:由于静态库已经被编译进程序中,因此可执行程序的体积比较大;
  • 程序更新麻烦:当静态库更新时,需要编译整个项目文件。

适用场景:静态库通常用于小型程序或嵌入式系统,因为它们可以减少程序的依赖,避免了动态库加载时的开销。

4.2 动态库

动态库(Dynamic Link Library,简称DLL)是一种在程序运行时才会被加载的库,程序需要调用其中的函数或者变量时才会进行加载。动态库的文件格式为.so(Linux).dll(Windows),可以被多个程序共享使用,因此可以减小可执行程序的体积。

优点

  • 程序体积小:多个程序可以共享一个库文件,减少了可执行程序的体积(即多个应用程序可以同时加载使用同一个动态库,而对于静态库,每个程序都有一份自己的静态库副本,无法共享)
  • 程序更灵活:程序在运行时才进行库的加载,便于程序的动态更新
  • 便于维护:在动态更新时秩序替换相应的库文件,不需要重新编译整个程序。(即当你改动动态库文件的内容时,比如你增加了某项功能,只需要重新生成对应的动态库文件,去替换旧的就行,不再需要将整个项目程序编译)
  • 节省内存:由于多个程序可以共享一个库文件,因此可以节省内存资源。

缺点

  • 链接时间长:由于库文件在程序运行时才会进行加载,因此会增加程序的启动时间;
  • 运行时可能会出现找不到库文件的错误
  • 对系统依赖:由于动态库在运行时才会进行加载,因此需要依赖相应的系统库,若系统库版本不兼容可能会出现错误
  • 程序安全性较差:由于动态库是公开的的,因此容易被黑客攻击

适用场景:动态库通常用于大型程序中,因为它们可以减少内存占用,允许多个程序共享同一份库代码。此外,动态库还允许在运行时加载插件和扩展,从而增强程序的灵活性和可扩展性。

总结

本次从ChatGPT中学习了动态库和静态库的使用,以及它们之间的区别和优缺点。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱听歌的周童鞋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值