【Linux】动态库与静态库

文章目录

  • 1. 认识静态库与动态库
  • 2. 手动创建并测试静态库
    • 2.1 生成静态库
    • 2.2 打包静态库
    • 2.3 使用静态库
  • 3. 库搜索路径
  • 4. 手动创建并测试动态库
    • 4.1 生成动态库
    • 4.2 打包动态库
    • 4.3 使用动态库
  • 5. 动静态库优先级

在这里插入图片描述

1. 认识静态库与动态库

  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
  • 一个动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)。
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制,允许物理内存中的一份动态库被 需要用到该库的所有进程 共用,节省了内存和磁盘空间。

本文测试准备代码:

// ---------- add.h ----------
#ifndef __ADD_H__
#define __ADD_H__
int add(int a, int b);
#endif// ---------- add.c ----------
#include "add.h"int add(int a, int b)
{return a + b;
}// ---------- sub.h ----------
#ifndef __SUB_H__
#define __SUB_H__
int sub(int a, int b);
#endif// ---------- sub.c ----------
#include "sub.h"int sub(int a, int b)
{return a - b;
}// ---------- main.c ----------
#include <stdio.h>
#include "add.h"
#include "sub.h"int main()
{int a = 10;int b = 20;printf("add(%d, %d) = %d\n", a, b, add(a, b));a = 100;b = 20;printf("sub(%d, %d) = %d\n", a, b, sub(a, b));return 0;
}

2. 手动创建并测试静态库

2.1 生成静态库

我们先使用命令生成一个静态库,并测试是否可以使用:

# 把上面的代码拿过来
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
total 20
-rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:13 add.c
-rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:13 add.h
-rw-rw-r-- 1 ubuntu ubuntu 243 Apr 20 20:33 main.c
-rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:15 sub.c
-rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:14 sub.h# 生成 .o 文件,准备打包为静态库
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -c add.c -o add.o
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -c sub.c -o sub.o# 生成静态库
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ar -rc libmymath.a add.o sub.o
# ar 是 gnu 归档工具,常用于将目标文件打包为静态库
# rc 表示(replace and create)# 查看静态库中的目录列表
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ar -tv libmymath.a 
rw-r--r-- 0/0   1376 Jan  1 08:00 1970 add.o
rw-r--r-- 0/0   1376 Jan  1 08:00 1970 sub.o
# t:列出静态库中的文件
# v:verbose 详细信息ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc main.c -L. -lmymath
# -L:指定库路径
# -l:指定库名# 测试静态库
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.c  add.h  add.o  a.out  libmymath.a  main.c  sub.c  sub.h  sub.o
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ./a.out 
add(10, 20) = 30
sub(100, 20) = 80# 测试目标文件生成后,静态库删掉,程序照样可以运行
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ rm libmymath.a 
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ./a.out 
add(10, 20) = 30
sub(100, 20) = 80

2.2 打包静态库

如果想把这个静态库给别人使用,我们需要将 库(.a)和 头文件(.h)一起打包;这样别人在看到头文件的时候,就知道我们的库中封装了哪些方法。

使用 Makefile 进行自动化编译:

# ---------- Makefile ----------
libmymath.a:sub.o add.oar -rc $@ $^%.o:%.cgcc -c $<.PHONY:clean
clean:rm -rf *.o output libmymath.a# 将相关文件打包到 output 文件夹
.PHONY:output
output:mkdir outputcp -rf *.h outputcp libmymath.a output
# ubuntu 20.04 实机演示ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
total 24
-rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:13 add.c
-rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:13 add.h
-rw-rw-r-- 1 ubuntu ubuntu 243 Apr 20 20:33 main.c
-rw-rw-r-- 1 ubuntu ubuntu 188 Apr 21 13:38 Makefile
-rw-rw-r-- 1 ubuntu ubuntu  61 Apr 20 20:15 sub.c
-rw-rw-r-- 1 ubuntu ubuntu  65 Apr 20 20:14 sub.h# 构建静态库
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make
gcc -c sub.c
gcc -c add.c
ar -rc libmymath.a sub.o add.o
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
total 36
-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:13 add.c
-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:13 add.h
-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 add.o
-rw-rw-r-- 1 ubuntu ubuntu 2960 Apr 21 13:43 libmymath.a
-rw-rw-r-- 1 ubuntu ubuntu  243 Apr 20 20:33 main.c
-rw-rw-r-- 1 ubuntu ubuntu  188 Apr 21 13:38 Makefile
-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:15 sub.c
-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:14 sub.h
-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 sub.o# 打包
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make output 
mkdir output
cp -rf *.h output
cp libmymath.a output
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
total 40
-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:13 add.c
-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:13 add.h
-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 add.o
-rw-rw-r-- 1 ubuntu ubuntu 2960 Apr 21 13:43 libmymath.a
-rw-rw-r-- 1 ubuntu ubuntu  243 Apr 20 20:33 main.c
-rw-rw-r-- 1 ubuntu ubuntu  188 Apr 21 13:38 Makefile
drwxrwxr-x 2 ubuntu ubuntu 4096 Apr 21 13:43 output
-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:15 sub.c
-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:14 sub.h
-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 sub.o# 查看 output
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree output/
output/
├── add.h
├── libmymath.a
└── sub.h0 directories, 3 files

2.3 使用静态库

我们已经将静态库以及相关头文件打包到了 output 文件夹,现在我们新建一个 TestStaticLib 文件夹进行测试:

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ mkdir TestStaticLib
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls -l
total 44
-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:13 add.c
-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:13 add.h
-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 add.o
-rw-rw-r-- 1 ubuntu ubuntu 2960 Apr 21 13:43 libmymath.a
-rw-rw-r-- 1 ubuntu ubuntu  243 Apr 20 20:33 main.c
-rw-rw-r-- 1 ubuntu ubuntu  188 Apr 21 13:38 Makefile
drwxrwxr-x 2 ubuntu ubuntu 4096 Apr 21 13:43 output
-rw-rw-r-- 1 ubuntu ubuntu   61 Apr 20 20:15 sub.c
-rw-rw-r-- 1 ubuntu ubuntu   65 Apr 20 20:14 sub.h
-rw-rw-r-- 1 ubuntu ubuntu 1376 Apr 21 13:43 sub.o
drwxrwxr-x 2 ubuntu ubuntu 4096 Apr 21 13:56 TestStaticLib# 将 output 移动到 TestStaticLib 文件夹,并改名为 StaticLib
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ mv output TestStaticLib/
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ cd TestStaticLib/
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ mv output StaticLib
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ls
StaticLib

TestStaticLib 目录中新建测试文件:

// ---------- test.c ----------
#include <stdio.h>
#include "add.h"
#include "sub.h"int main()
{int a = 10;int b = 20;printf("add(%d, %d) = %d\n", a, b, add(a, b));a = 100;b = 20;printf("sub(%d, %d) = %d\n", a, b, sub(a, b));return 0;
}

使用 Makefile 进行自动化编译:

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ls
StaticLib  test.c
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ touch Makefile# 编写 Makefile
ubuntu:~/Linux/test_4_20/TestStaticLib$ vim Makefile
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ cat Makefile 
test:test.cgcc -o $@ $^ -I ./StaticLib -L ./StaticLib -l mymath# -I:指定头文件所在目录# -L:指定静态库所在目录# -l:指定库名.PHONY:clean
clean:rm -f test

测试:

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ make
gcc -o test test.c -I ./StaticLib -L ./StaticLib -l mymath
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ls -l
total 32
-rw-rw-r-- 1 ubuntu ubuntu    99 Apr 21 14:21 Makefile
drwxrwxr-x 2 ubuntu ubuntu  4096 Apr 21 13:43 StaticLib
-rwxrwxr-x 1 ubuntu ubuntu 16816 Apr 21 14:21 test
-rw-rw-r-- 1 ubuntu ubuntu   243 Apr 21 14:13 test.c
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestStaticLib$ ./test 
add(10, 20) = 30
sub(100, 20) = 80

3. 库搜索路径

  • 从左到右搜索 -L 指定的目录
  • 由环境变量指定的目录(LIBRARY_PATH)
  • 由系统指定的目录
    • / user / lib
    • / user / local / lib

下面测试使用动态库时,需要我们在库搜索路径中手动添加动态库!

4. 手动创建并测试动态库

4.1 生成动态库

使用命令手动生成静态库:

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.c  add.h  main.c  sub.c  sub.h
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -fPIC -c sub.c add.c
# fPIC:产生位置无关码(position independent code)
# 编译产生的代码没有绝对位置,只有相对位置,从而可以在任意地方调用生成的动态库ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.c  add.h  add.o  main.c  sub.c  sub.h  sub.o
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ gcc -shared -o libmymath.so *.o
# shared:表示生成共享库格式
# 库名规则:lib*.soubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.c  add.h  add.o  libmymath.so  main.c  sub.c  sub.h  sub.o

4.2 打包动态库

将 动态库(.so)和 头文件(.h)打包到一个文件夹:

# ---------- Makefile ----------
libmymath.so:add.o sub.ogcc -shared -o $@ $^%.o:%.cgcc -fPIC -c $<.PHONY:clean
clean:rm -rf libmymath.so *.o DynamicLib.PHONY:DynamicLib
DynamicLib:mkdir DynamicLibcp *.h DynamicLibcp libmymath.so DynamicLib
# ubuntu 20.04 实机演示# 构建动态库
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.c  add.h  main.c  Makefile  sub.c  sub.h  TestStaticLib
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make
gcc -fPIC -c add.c
gcc -fPIC -c sub.c
gcc -shared -o libmymath.so add.o sub.o
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.c  add.o         main.c    sub.c  sub.o
add.h  libmymath.so  Makefile  sub.h  TestStaticLib# 打包
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ make DynamicLib 
mkdir DynamicLib
cp *.h DynamicLib
cp libmymath.so DynamicLib
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ ls
add.c  add.o       libmymath.so  Makefile  sub.h  TestStaticLib
add.h  DynamicLib  main.c        sub.c     sub.o# 查看打包文件
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree DynamicLib/
DynamicLib/
├── add.h
├── libmymath.so
└── sub.h0 directories, 3 files

4.3 使用动态库

故技重施,创建 TestDynamicLib 文件夹进行测试:

# 把上面的动态库拿过来,并创建 Makefile 和 test.c
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ tree TestDynamicLib/
TestDynamicLib/
├── DynamicLib
│   ├── add.h
│   ├── libmymath.so
│   └── sub.h
├── Makefile
└── test.c
# test.c 与静态库保持一致# Makefile
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ cat TestDynamicLib/Makefile 
test:test.cgcc -o $@ $^ -I ./DynamicLib -L ./DynamicLib -l mymath.PHONY:clean
clean:rm -f test

直接编译试试?

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20$ cd TestDynamicLib/# 编译
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ make
gcc -o test test.c -I ./DynamicLib -L ./DynamicLib -l mymath
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ls
DynamicLib  Makefile  test  test.c# 运行
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ./test 
./test: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory

发现报错了,系统找不到我们的动态库,为什么?

上面我们提到过库搜索路径,我们只是在编译时指定了路径,只有编译器知道动态库的位置,编译完成后就没有人知道去哪里找动态库了!所以可执行程序是找不到动态库的!

那为什么静态库没事呢?因为静态库是直接把库链接到可执行文件中,编译完成后把静态库删掉都不会影响可执行文件

动态库不一样,程序每次执行都要去磁盘上搜索动态库的位置,然后链接,由于我们并没有指定动态库的位置,所以找不到!

如何解决?

一个比较简单且没有副作用的方法是修改 LD_LIBRARY_PATH 环境变量,因为我们只是想测试,并不想真的把这个动态库添加到系统中,而环境变量在我们关掉 xshell 后就会重置,所以我们选择这种方法进行测试:

ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ pwd
/home/ubuntu/Linux/test_4_20/TestDynamicLib# 修改环境变量
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ export LD_LIBRARY_PATH=/home/ubuntu/Linux/test_4_20/TestDynamicLib/DynamicLib# 查看环境变量
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ echo $LD_LIBRARY_PATH
/home/ubuntu/Linux/test_4_20/TestDynamicLib/DynamicLib
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ls
DynamicLib  Makefile  test  test.c# 执行测试程序
ubuntu@VM-20-5-ubuntu:~/Linux/test_4_20/TestDynamicLib$ ./test 
add(10, 20) = 30
sub(100, 20) = 80# 测试成功

5. 动静态库优先级

  • 如果我们同时提供动态库和静态库,gcc 默认使用的是动态库;
  • 如果我们非要静态链接,我们必须使用 static 选项;
  • 如果我们只提供静态库,那我们的可执行程序也没办法,只能对该库进行静态链接,但是程序不一定整体是静态链接的。
  • 如果我们只提供动态库,默认只能动态链接,如果非要静态链接,会发生链接报错!

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/2551.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于深度学习网络的十二生肖图像分类matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ............................................................... for i 1:16subplot(4,4,…

真的通俗易懂!差分信号电路的解读

目录 一、什么是差分运放电路 二、差分运放电路的工作状态 一、什么是差分运放电路 差分电路是具有对共模信号抑制&#xff0c;对差模信号放大特征的电路。该电路的两个信号输信号的差值是该电路的有效值。将这两信号输入只差进行放大后输出。如果存在干扰信号&#xff0c;会…

H264 编码标准常见术语解释

H264 编码标准 H.264编码标准&#xff0c;也被称作MPEG-4 AVC&#xff08;Advanced Video Coding&#xff09;&#xff0c;是一种被广泛使用的数字视频压缩标准&#xff0c;由国际电信联盟&#xff08;ITU-T&#xff09;和国际标准化组织&#xff08;ISO&#xff09;共同开发。…

如何确定IP地址的地理位置

IP地址的地理位置确定是一个复杂而精细的过程&#xff0c;它结合了多种技术与方法来推断或确定设备在网络中的大致物理位置。以下是对IP地址地理位置确定过程的详细解释&#xff1a; 首先&#xff0c;我们要理解IP地址本身并不能直接反映物理位置信息。IP地址主要是用于在网络中…

vscode将本地服务转发到外网地址访问

示例中将本地的5500端口&#xff0c;用vscode进行端口转发&#xff0c;在外网地址访问服务 要转发的端口 转发端口 点击转发端口 输入要转发的端口&#xff0c;按下回车 Enter 点击允许&#xff0c;弹出确认界面后点击打开 转发端口已经成功配置上&#xff0c;右键可见性…

栈和队列-介绍与实现(超级!!!详解-C语言)

目录 栈 栈的介绍 栈的概念 栈的结构 栈的实现 初始化栈 StackInit 销毁栈 StackDestroy 入栈 StackPush 出栈 StackPop 获取栈顶元素 StackTop 检查栈是否为空 StackEmpty 获取栈中有效元素个数 StackSize 队列 队列的介绍 队列的概念 队列的结构 队列的应用 队列的实现 …

建议收藏!网络安全入门知识汇总,自学必看!

计算机网络的广泛应用&#xff0c;为人们的生产、生活、工作、娱乐带来了方便&#xff0c;同时由于技术原因和人为因素&#xff0c;也为人们带来诸多安全隐患。这催发出一个新的职业——网络安全工程师。 目前网络安全工程师变得越来越重要&#xff0c;很多人也开始对网络安全…

jsp实验11 JavaBean

二、实验项目内容&#xff08;实验题目&#xff09; 编写代码&#xff0c;掌握javabean的用法。【参考课本 上机实验 5.5.2 】 三、源代码以及执行结果截图&#xff1a; 源代码&#xff1a; Memory.java package sea.water; import java.util.ArrayList; import java.util…

280 Stylized Desert Beach Textures - Sand Cracked Sand Water More v1.1.0

280多种风格化的沙子、破裂的沙子、土壤、沙质岩石和其他沙质纹理的集合,用于沙漠和海滩风格化/幻想/rpg风格的游戏环境。 这款由game Buffs设计的280多种风格化沙漠和海滩纹理系列,为您的游戏锦上添花! 在这个系列中,你会在风格化/幻想/rpg风格的游戏中找到大量适合沙漠、…

python与上位机开发day02

1.常见运算符 1.1 赋值运算符 赋值运算符主要用来对变量进行赋值,包括如下这些: 运算符描述赋值加等于-减等于*乘等于/除等于//整除等于%模等于**幂等于 实例如下: a 10 a 5 # 等价于 a a5 a *2 # 等价于 a a*21.2 比较运算符 比较运算符主要用来比较两个数据的大小…

树莓派驱动开发----iic驱动oled屏幕篇

水一期吧&#xff0c;上效果 有点模糊&#xff0c;我直接说吧&#xff0c;修改设备树&#xff0c;iic1&#xff0c;地址0x3c&#xff0c;然后编写驱动文件&#xff0c;app文件&#xff0c;挂载驱动模块后在终端输入 /*******************************************************…

Ventus(承影):基于RISC V的开源GPGPU

Ventus&#xff08;承影&#xff09;&#xff1a;基于RVV的开源GPGPU 清华大学集成电路学院dsp-lab的承影RVV GPGPU设计文档。 整体目标 提供一个开源的基于RVV的GPGPU实现方案&#xff0c;并给出软件映射方案、指令集&#xff08;支持的指令及特性、添加的自定义指令&#xf…

经典的目标检测算法有哪些?

一、经典的目标检测算法有哪些&#xff1f; 目标检测算法根据其处理流程可以分为两大类&#xff1a;One-Stage&#xff08;单阶段&#xff09;算法和Two-Stage&#xff08;两阶段&#xff09;算法。以下是一些经典的目标检测算法&#xff1a; 单阶段算法: YOLO (You Only Loo…

iOS ------代理 分类 拓展

代理协议 一&#xff0c;概念&#xff1a; 代理&#xff0c;又称委托代理&#xff08;delegate&#xff09;&#xff0c;是iOS中常用的一种设计模式。顾名思义&#xff0c;它是把某个对象要做的事委托给别的对象去做。那么别的对象就是这个对象的代理&#xff0c;代替它来打理…

图书租赁系统-借阅图书

图中展示了所有可以借阅的图书&#xff0c;点击“借阅”按钮便可以借阅图书。 借阅成功后&#xff0c;可以到bookorder菜单中阅读该书。 阅读功能待开发。 add.html借阅图书页面 <!DOCTYPE html> <html lang"zh" xmlns:th"http://www.thymeleaf.org…

学习经验分享【33】YOLOv5 / YOLOv7 / YOLOv8 / YOLOv9 / RTDETR 基于 Pyside6 的图形化界面

大论文可以写两章关于算法创新模型&#xff0c;最后一章可以写对前两章提出方法进行封装&#xff0c;利用PyQT5搭建YOLOv5可视化界面&#xff0c;并打包成exe程序&#xff0c;构建检测平台实现简单的应用。用来凑大论文的字数和工作量&#xff0c;是简单又快速的方法&#xff0…

如何使用国内手机号免费注册一个美区 Apple ID?

因为一些众所周知的原因&#xff0c;在国内使用 iPhone 是被阉割过的&#xff0c;如果想要用完全版就需要用到美区账号&#xff0c;废话不多说直接上图。 在 iPhone 的浏览器上打开链接进行注册 https://appleid.apple.com/account 如果注册提示&#xff1a;Your request cou…

SpringCloud注册nacos错误:Could not resolvplaceholder ‘xxxxx‘ in value “xxxx“

这个错误是我在做spirngcloud注册服务到nacos时发现的&#xff0c;算是折磨我折磨了好久&#xff0c;最后发现了还是先记录一下&#xff0c;首先还是说一下我的项目版本信息&#xff0c;因为不同的版本就有这不同的解决方案&#xff0c;这也是最恶心的一点&#xff0c;以至于我…

基于RT-Thread的智能家居助手

一、项目简介 智能家居助手主要基于RT-Thread开发的&#xff0c;该系统主要分为语音子系统&#xff0c;环境监测子系统&#xff0c;智能控制子系统&#xff0c;智能网关子系统&#xff0c;音乐播放器&#xff0c;云端以及应用软件七大部分。语音子系统可通过语音进行人机交互来…

OpenCV——图像分块局部阈值二值化

目录 一、算法原理1、算法概述2、参考文献 二、代码实现三、结果展示 OpenCV——图像分块局部阈值二值化由CSDN点云侠原创&#xff0c;爬虫自重。如果你不是在点云侠的博客中看到该文章&#xff0c;那么此处便是不要脸的爬虫。 一、算法原理 1、算法概述 针对目前局部阈值二值…