『Linux从入门到精通』第 ㉒ 期 - 动静态库

在这里插入图片描述

文章目录

  • 💐专栏导读
  • 💐文章导读
  • 🐧什么是库?
  • 🐧为什么要有库?
  • 🐧写一个自己的库
    • 🐦方法一
    • 🐦方法二 静态库
    • 🐦标准化
    • 🐦方法三 动态库
    • 🐦配置动态库
      • 🐱环境变量
      • 🐱软链接
      • 🐱配置文件
  • 🐧静态库与动态库的区别
  • 🐧动态库的运作原理
      • 🐔为什么进程可以在运行时加载动态库?
      • 🐔为什么多个进程可以共享一个动态库

💐专栏导读

🌸作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。

🌸专栏简介:本文收录于 Linux从入门到精通,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。

🌸相关专栏推荐:C语言初阶系列C语言进阶系列C++系列数据结构与算法

💐文章导读

本章我们将深入学习Linux中动静态库的使用及其原理。

在这里插入图片描述

🐧什么是库?

在学习生涯中,我们总是能谈到库,例如C语言标准库、C++标准库。那么到底什么是库呢?

在计算机科学中,术语“库”通常指的是库文件(Library),它是一组预编译的、可重用的代码和资源的集合,用于支持软件开发。库的目的是为开发人员提供一组常用的功能,以便在应用程序中进行调用,从而避免重复编写相同的代码。

库可以分为两大类:

  • 静态库(Static Library): 静态库在编译时被链接到应用程序中,形成一个独立的可执行文件。在程序运行时,静态库的代码被完全复制到应用程序中,因此应用程序不再依赖于原始的库文件。静态库的文件扩展名通常是.a(在Unix/Linux系统中)或.lib(在Windows系统中)。

  • 动态库(Dynamic Library): 动态库在运行时加载到内存中,多个应用程序可以共享同一个动态库的实例。这可以减小应用程序的大小,因为动态库的代码只需要存在一份,而且可以在运行时更新。动态库的文件扩展名通常是.so(在Unix/Linux系统中)或.dll(在Windows系统中)。

在编程中,开发人员通过包含库的头文件、链接库文件,以及在代码中调用库提供的函数或方法,可以轻松地利用库的功能。

在我们的Linxu机器上,系统已经为我们预装了C/C++的头文件和库文件。头文件提供方法说明,库文件提供方法的实现。头文件与库文件是有对应关系的,需要组合在一起使用。

一个可执行程序生成需要经历四个阶段:预处理、编译、汇编、链接。头文件在预处理阶段被引入,库文件在链接阶段被引入。

C/C++库文件

$ ls /usr/lib64/libc*
/usr/lib64/libc-2.17.so         /usr/lib64/libc_nonshared.a        /usr/lib64/libcroco-0.6.so.3.0.1    /usr/lib64/libc.so
/usr/lib64/libcap-ng.so.0       /usr/lib64/libcollection.so.2      /usr/lib64/libcrypt-2.17.so         /usr/lib64/libc.so.6
/usr/lib64/libcap-ng.so.0.0.0   /usr/lib64/libcollection.so.2.1.1  /usr/lib64/libcrypto.so.10          /usr/lib64/libcupscgi.so.1
/usr/lib64/libcap.so.2          /usr/lib64/libcom_err.so.2         /usr/lib64/libcrypto.so.1.0.2k      /usr/lib64/libcupsimage.so.2
/usr/lib64/libcap.so.2.22       /usr/lib64/libcom_err.so.2.1       /usr/lib64/libcryptsetup.so.12      /usr/lib64/libcupsmime.so.1
/usr/lib64/libcgroup.so.1       /usr/lib64/libcpupower.so.0        /usr/lib64/libcryptsetup.so.12.3.0  /usr/lib64/libcupsppdc.so.1
/usr/lib64/libcgroup.so.1.0.41  /usr/lib64/libcpupower.so.0.0.0    /usr/lib64/libcryptsetup.so.4       /usr/lib64/libcups.so.2
/usr/lib64/libcidn-2.17.so      /usr/lib64/libcrack.so.2           /usr/lib64/libcryptsetup.so.4.7.0   /usr/lib64/libcurl.so.4
/usr/lib64/libcidn.so           /usr/lib64/libcrack.so.2.9.0       /usr/lib64/libcrypt.so              /usr/lib64/libcurl.so.4.3.0
/usr/lib64/libcidn.so.1         /usr/lib64/libcroco-0.6.so.3       /usr/lib64/libcrypt.so.1

C/C++头文件

$ ls /usr/include/stdio.h
/usr/include/stdio.h
$ ls /usr/include/c++/4.8.5/iostream 
/usr/include/c++/4.8.5/iostream

🐧为什么要有库?

引入库的概念有很多重要的原因,它们有助于提高软件开发的效率、可维护性和可扩展性。以下是一些主要的原因:

  1. 代码重用: 库提供了一组通用的功能或工具,可以在多个项目中被重复使用。这样可以避免开发人员重复编写相同的代码,提高了开发效率。

  2. 模块化开发: 库使软件能够以模块化的方式构建。通过将不同的功能分解成独立的库,开发人员可以更容易地理解和维护代码。模块化开发还使得团队能够并行工作,每个成员专注于特定的任务或功能。

  3. 抽象和封装: 库提供了对底层实现的抽象,使开发人员可以专注于高层次的问题而不必关心底层的细节。这种抽象和封装的概念有助于隐藏复杂性,提高代码的可读性和可维护性。

  4. 提高可靠性: 库经过充分测试和验证,可以提供高质量的代码。开发人员使用库时,可以信任这些已经验证过的功能,减少了潜在的错误和漏洞。

  5. 快速开发: 使用库可以加速开发过程。通过利用现成的库,开发人员可以更快地构建应用程序,而不必从头开始编写每一个功能。

  6. 标准化: 某些库成为行业或社区标准,提供了一致的接口和实现。这种标准化有助于确保代码的一致性,同时使得不同项目之间更容易进行集成和交互。

  7. 可扩展性: 库使得软件的架构更具扩展性。通过将不同的模块组织为库,可以更容易地添加新功能、升级现有功能或替换特定的实现,而无需修改整个应用程序。

总体而言,引入库的概念有助于构建更可靠、可维护和可扩展的软件系统,提高了软件开发的效率和质量。

🐧写一个自己的库

为了更深入的理解库运作的原理,我们尝试自己写一个库,并交给其他小伙伴使用。

接下来,我们将实现一个加减运算的程序,并将给程序的源代码与头文件进行打包,并交给小伙伴——小黑使用。

$ touch add.c
$ touch add.h
$ touch sub.c
$ touch sub.h
/* add.h */
#pragma once 
int add(int a, int b);
/* add.c */
#include "add.h"
int add(int a, int b){return a + b;
}
/* sub.h */
#pragma once 
int sub(int a, int b);
/* sub.c */
#include "sub.h"
int sub(int a, int b){return a - b;
}

现在我们已经将计算器的源代码写好,现在我们想让小黑使用我们的成果,倘若我们直接把源文件以及头文件发给小黑,这种做法肯定是没问题的。

但是我们又想让小黑使用我们的成果,又不想让小黑看到我们的源代码,现在该怎么办呢?

🐦方法一

第一种方法是我们可以将源代码经过预处理、编译、汇编后形成二进制文件。小黑拿到该二进制文件后,再将它自己写的程序同样经过预处理、编译、汇编形成二进制文件,然后将两个二进制文件进行链接即可。

$ gcc -c *.c
$ ll
total 28
-rw-rw-r-- 1 hxy hxy   60 Feb 28 16:25 add.c
-rw-rw-r-- 1 hxy hxy   38 Feb 28 16:25 add.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:40 add.o
-rw-rw-r-- 1 hxy hxy   60 Feb 28 16:20 sub.c
-rw-rw-r-- 1 hxy hxy   39 Feb 28 16:19 sub.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:40 sub.o
drwxrwxr-x 2 hxy hxy 4096 Feb 28 16:40 xiaohei
$ cp *h xiaohei/
$ cp *o xiaohei//*小黑视角*/
ll
total 20
-rw-rw-r-- 1 hxy hxy   38 Feb 28 16:43 add.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:45 add.o
-rw-rw-r-- 1 hxy hxy  200 Feb 28 16:26 main.c
-rw-rw-r-- 1 hxy hxy   39 Feb 28 16:43 sub.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:45 sub.o
/*小黑视角*/
$ gcc -c main.c 
$ gcc -o test add.o sub.o main.o
$ ls
add.h  add.o  main.c  main.o  sub.h  sub.o  test
$ ./test 
10 + 3 = 13
10 - 3 = 7

🐦方法二 静态库

方法一中我们需要将许多的 .o文件以及.h文件打包给对方,这种做法明显感觉不是特别优雅。接下来我们就是用静态库的方式。

  1. 先将我们的.o文件打包生成一个静态库,并发送给小黑;
$ ar -rc libcalculate.a *.o
$ ll
total 32
-rw-rw-r-- 1 hxy hxy   60 Feb 28 16:25 add.c
-rw-rw-r-- 1 hxy hxy   38 Feb 28 16:25 add.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:40 add.o
-rw-rw-r-- 1 hxy hxy 2688 Feb 28 18:31 libcalculate.a
-rw-rw-r-- 1 hxy hxy   60 Feb 28 16:20 sub.c
-rw-rw-r-- 1 hxy hxy   39 Feb 28 16:19 sub.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:40 sub.o
drwxrwxr-x 2 hxy hxy 4096 Feb 28 18:23 xiaohei
$ cp libcalculate.a xiaohei/* 小黑视角 */
$ ll
total 16
-rw-rw-r-- 1 hxy hxy   38 Feb 28 18:35 add.h
-rw-rw-r-- 1 hxy hxy 2688 Feb 28 18:33 libcalculate.a
-rw-rw-r-- 1 hxy hxy  200 Feb 28 16:26 main.c
-rw-rw-r-- 1 hxy hxy   39 Feb 28 18:35 sub.h

注意

这里我们需要注意库的命名规则。库的命名是以lib为开头,以.a或.so为结尾。例如 libcalculate.a 的真实名称为 calculate

在小黑拿到我们的库文件后,他就可以编译生成自己的程序了。但是这里有几个细节需要注意:

  • 因为我们的库是第三方的,编译器并不知道这个库的存在,所以我们需要指明库所在的路径;
  • 同样,我们需要告诉编译器该链接哪一个库;
  • 同理,我们还需指明头文件所在的路径。但是目前头文件就在当前路径下,所以可省略;

2.小黑进行编译链接;

/* 小黑视角 */
$ gcc -o test main.c -L . -l calculate -I .
$ ./test 
10 + 3 = 13
10 - 3 = 7
  • -L 选项:指明库所在的路径;
  • -l 选项: 告诉编译器链接哪一个库;
  • -I 选项:告诉编译器头文件的位置;

🐦标准化

上面方法二中我们演示了一个库文件的使用原理。在实际的项目开发中,我们并不会这么随意潦草。
再以小黑为例:

  1. 将头文件全部移至一个目录下;
  2. 将库文件全部移至一个目录下;
  3. 将头文件与库文件进行打包;
  4. 将打包好的文件上传至云端;
$ mkdir lib
$ mkdir include
$ cp *.h include/
$ cp *.a lib
$ tar -czf calcuate.tgz include lib

远在海外的小黑想用我们写好的库,于是在云端将压缩包下载到了本地;

/* 小黑视角 */
$ ll
total 8
-rw-rw-r-- 1 hxy hxy 788 Feb 28 19:01 calcuate.tgz
-rw-rw-r-- 1 hxy hxy 200 Feb 28 16:26 main.c

小黑将它进行解压看到了头文件与库文件;

$ tar xzf calcuate.tgz 
$ ll
total 16
-rw-rw-r-- 1 hxy hxy  788 Feb 28 19:01 calcuate.tgz
drwxrwxr-x 2 hxy hxy 4096 Feb 28 18:56 include
drwxrwxr-x 2 hxy hxy 4096 Feb 28 18:56 lib
-rw-rw-r-- 1 hxy hxy  200 Feb 28 16:26 main.c

最后小黑进行了编译链接等一系列操作,成功运行了自己的程序;

$ gcc -o test main.c -I ./include -L ./lib -l calculate
$ ./test 
10 + 3 = 13
10 - 3 = 7

以后的小黑会经常用到这个库,但是他觉得每次都要写这么长的指令有些麻烦。于是他将这个库的头文件全部移至系统的/usr/include目录下;将库文件移至/usr/lib目录下;

$ sudo cp include/*.h /usr/include/
$ sudo cp lib/* /lib64/

以后他每次使用这个库时,编译器会自动在这两个目录下寻找所程序所依赖的头文件与库文件;

$ gcc -o test main.c -lcalculate
$ ./test 
10 + 3 = 13
10 - 3 = 7

🐦方法三 动态库

以上我们尝试将自己的源文件制作为一个静态库供小黑使用,接下来我们在尝试制作一个动态库。

$ gcc -fPIC -c add.c sub.c
  • fPIC:产生位置无关码(position independent code);
gcc -shared -o libcalculate.so *.o
  • shared: 表示生成共享库格式;

接着我们把生成的.so文件放在lib目录下,将.h文件放到include目录下,并打包发给小黑(重复之前的操作)。

$ rm lib/libcalculate.a
$ rm calcuate.tgz 
$ cp *.so lib
$ tar -czf calculate.tgz lib include
$ cp calculate.tgz xiaohei/

小黑拿到压缩文件,解压后得到libinclude于是用我们的库来链接自己的程序。

/*小黑视角*/
$ tar xzf calculate.tgz 
$ ll
total 16
-rw-rw-r-- 1 hxy hxy 2343 Feb 29 16:39 calculate.tgz
drwxrwxr-x 2 hxy hxy 4096 Feb 28 18:56 include
drwxrwxr-x 2 hxy hxy 4096 Feb 29 16:38 lib
-rw-rw-r-- 1 hxy hxy  200 Feb 28 16:26 main.c
$
$ gcc -o test main.c -I include -L lib -lcalculate
$ ll
total 28
-rw-rw-r-- 1 hxy hxy 2343 Feb 29 16:39 calculate.tgz
drwxrwxr-x 2 hxy hxy 4096 Feb 28 18:56 include
drwxrwxr-x 2 hxy hxy 4096 Feb 29 16:38 lib
-rw-rw-r-- 1 hxy hxy  200 Feb 28 16:26 main.c
-rwxrwxr-x 1 hxy hxy 8432 Feb 29 16:49 test
$
$ ./test 
./test: error while loading shared libraries: libcalculate.so: cannot open shared object file: No such file or directory

小黑发现了事情的不妙,心想刚才不是还好好的吗?怎么运行时提示找不到库文件呢?

原来是因为在程序运行时,calculate.so并没有在系统的默认路径下,所以OS找不到!那么如何才能让OS找到我们的库呢?答案是需要我们自己来配置。

🐦配置动态库

配置动态库有三种方法:

  1. 环境变量:LD_LIBRARY_PATH (临时方案);
  2. 软链接方案;
  3. 配置文件方案

🐱环境变量

导入环境变量LD_LIBRARY_PATH:

$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/..../lib/ # 你的.so文件存放路径

查看环境变量LD_LIBRARY_PATH:

$ echo $LD_LIBRARY_PATH
:/usr/local/protobuf/lib/:/home/hxy/.VimForCpp/vim/bundle/YCM.so/el7.x86_64:/home/..../lib/ # 你的.so

现在再来运行程序就可以成功运行了:

$ ./test 
10 + 3 = 13
10 - 3 = 7

🐱软链接

上面我们说在程序运行时,calculate.so并没有在系统的默认路径下,所以OS找不到我们的库,那么这个默认路径在哪里呢?

# 一般在这两个路径下
$ /lib
$ /lib64/

所以我们直接将库文件移动到这两个路径下也可以,但是还有比较优雅一点的方案,那就是为我们的库文件建立软链接。

$ sudo ln -s lib/libcalculate.so /lib64/libcalculate.so
$ ./test 
10 + 3 = 13
10 - 3 = 7

🐱配置文件

  1. 在系统的 /etc/ld.so.conf.d/ 目录下创建一个配置文件;
$ sudo touch /etc/ld.so.conf.d/calculate.conf
  1. 将动态库所在路径写入配置文件;
$ sudo vim /etc/ld.so.conf.d/calculate.conf
$ sudo cat /etc/ld.so.conf.d/calculate.conf
/home/.../lib # 你的.so文件路径
  1. 让配置文件生效;
$ sudo ldconfig

现在小黑的程序也能成功运行了;

$ ./test 
10 + 3 = 13
10 - 3 = 7

🐧静态库与动态库的区别

以上我们通过制作一个自己的静态库,对库文件有了基础的了解。那么库为何又要分为静态库与动态库呢?二者有何区别?

  1. 链接时机和方式:

    • 静态库: 在编译时被链接到目标程序中,链接器将库的代码和数据拷贝到最终的可执行文件中。因此,可执行文件在运行时独立于库文件。
    • 动态库: 在编译时并不直接链接到目标程序,而是在运行时由操作系统动态加载到内存中。动态库的链接发生在程序启动时(静态加载)或在运行时(动态加载)。
  2. 文件大小和内存占用:

    • 静态库: 链接时会将库的代码和数据完全复制到目标程序中,可能导致可执行文件较大。每个使用该库的可执行文件都包含一份库的拷贝。
    • 动态库: 多个程序可以共享同一个动态库的实例,因此相同的库只需要在内存中存在一份,可以减小程序的大小。
  3. 更新和维护:

    • 静态库: 如果库的代码或数据发生变化,需要重新编译并重新链接所有使用该库的程序。每个程序都需要更新以包含最新版本的库。
    • 动态库: 如果库的代码或数据发生变化,只需要替换库文件而无需重新编译和链接使用该库的程序。这使得动态库更容易更新和维护。
  4. 跨平台兼容性:

    • 静态库: 可执行文件与库的链接是在编译时完成的,因此在不同平台上可能需要不同版本的库。
    • 动态库: 由于动态库的加载是在运行时由操作系统完成的,因此相同的动态库文件可以在多个平台上使用。
  5. 运行时灵活性:

    • 静态库: 执行文件在编译时固定了对静态库的依赖,无法在运行时更改。
    • 动态库: 可以在运行时加载或替换动态库,这使得系统更加灵活。

🐧动态库的运作原理

🐔为什么进程可以在运行时加载动态库?

我们知道每个程序在运行时就变成一个进程,一个进程拥有自己的虚拟地址空间。

在程序运行时,我们只需要将库加载到内存当中,经过页表映射到进程的地址空间中,我们的代码执行库中的方法就依旧还是在自己的地址空间中进行函数跳转。

🐔为什么多个进程可以共享一个动态库

当多个进程同时运行时,按照同样的方式,将库中的地址映射到每个进程的地址空间中,那么如果每个程序使用的地址都是相同的,不会产生冲突吗?

还记得我们在用 gcc 生成动态库时用到的参数 - fPIC 吗?

-fPIC 是 GCC 编译器选项,用于生成位置无关码(Position Independent Code,PIC)。位置无关码是一种在内存中加载时不依赖于特定内存地址的机器码,通常用于共享库(动态链接库)的编译。

具体来说,使用 -fPIC 选项的目的是允许将生成的目标文件用于共享库,而这些库可以被多个进程加载到内存的不同地址上,而不会发生地址冲突。

它主要特点包括:

  1. 位置独立性: 生成的代码不依赖于特定的内存地址,可以在不同的内存地址空间中运行。这对于动态链接库是必要的,因为它们可能在不同的进程中加载并映射到不同的地址。

  2. 全局偏移表(Global Offset Table,GOT): 在运行时,PIC 代码使用全局偏移表,其中包含指向全局和共享库中的符号的指针。这些指针在加载库时进行重定位,以便正确地找到符号的位置。

  3. 避免绝对地址: 使用相对寻址或基于 GOT 的寻址,而不是绝对地址。这使得代码更容易在不同的地址空间中重定位。

本章的内容到这里就结束了!如果觉得对你有所帮助的话,欢迎三连~

在这里插入图片描述

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

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

相关文章

python中自定义报错

class MyError(Exception):def __init__(self,num):#录入的数Exception.__init__(self)self.numnumdef __str__(self):return 这是我定义的第%d个异常 %(self.num)使用 try:raise MyError(4) except MyError as e:print(e)raise 其作用是指定抛出的异常名称,以及异常…

数据结构题目①——数组

前言 本篇文章为博主进行代码随想录——数组练习后的总结会涉及到每一道题目的详细的思路整理,以及本人的易错点,希望对大家有所帮助 数组介绍: 数组在C语言中就已经有所涉及,它是一个最基础的数据结构,而在数据结构中…

three 层级模型

group.remove(mesh1,mesh2);Vector3与模型位置、缩放属性 Group层级模型(树结构) 创建了两个网格模型mesh1、mesh2,通过THREE.Group类创建一个组对象group,然后通过add方法把网格模型mesh1、mesh2作为设置为组对象group的子对象,然后在通过执行scene.a…

jenkins部署maven项目

流程: jenkins从代码仓库读取代码,将代码文件放入jenkins的工作空间,将jenkins工作空间的代码进行打包,将jar包远程发送给服务器。 一:所需插件二:Tools 三:System: 配置ssh连接的…

python63-Python的循环之循环使用else

Python的循环都可以定义else代码块&#xff0c;当循环条件为False 时&#xff0c;程序会执行else代码块。如下代码示范了为while循环定义else代码块。 # !/usr/bin/env python# -*- coding: utf-8 -*-# Time : 2024/01# Author : Laopicount_i 0while count_i < 5:print(c…

Java集合相关面试题(2024大厂高频面试题系列)

1、说一说Java提供的常见集合&#xff1f;&#xff08;画一下集合结构图&#xff09; 在java中提供了量大类的集合框架&#xff0c;主要分为两类&#xff1a; 第一个是Collection 属于单列集合&#xff0c;第二个是Map 属于双列集合 在Collection中有两个子接口List和Set。…

【力扣hot100】刷题笔记Day14

前言 又是新的一周&#xff0c;快乐的周一&#xff0c;快乐地刷题&#xff0c;今天把链表搞完再干活&#xff01; 114. 二叉树展开为链表 - 力扣&#xff08;LeetCode&#xff09; 前序遍历 class Solution:def flatten(self, root: Optional[TreeNode]) -> None:if not r…

【IC前端虚拟项目】inst_buffer子模块DS与RTL编码

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 需要说明一下的是,在我所提供的文档体系里,并没有模块的DS文档哈,因为实际项目里我也不怎么写DS毕竟不是每个公司都和HISI一样对文档要求这么严格的。不过作为一个培训的虚拟项目,还是建议在时间充裕…

Docker技术概论(3):Docker 中的基本概念

Docker技术概论&#xff08;3&#xff09; Docker 中的基本概念 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://…

基于java+springboot女士电商平台系统源码+文档设计

基于javaspringboot女士电商平台系统源码文档设计 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末获取源…

C语言----动态内存管理(2)

1.这里总结动态内存管理里面的错误 &#xff08;1&#xff09;使用malloc开辟空间以后直接赋值 这个就是malloc开辟失败返回空指针&#xff0c;直接给空指针赋值就是错误的&#xff0c; tip1:使用malloc开辟空间以后一定要判断是否为空 &#xff08;2&#xff09; 越界访问…

Python批量提取文件夹中图片的名称及路径到指定的.txt文件中

目录 一、代码二、提取效果 一、代码 import os# 定义要保存的文件名 file_name "TestImage/Image_Visible_Gray.txt"# 读取文件夹路径 folder_path "TestImage/Image_Visible_Gray"# 遍历文件夹中的所有文件 with open(file_name, "w") as f…

可移植性(兼容性)测试指南

可移植性是指应用程序能够安装到不同的环境中&#xff0c;在不同的环境中使用&#xff0c;甚至可以移动到不同的环境中。当然&#xff0c;前两者对所有系统都很重要。就PC软件而言&#xff0c;鉴于操作系统、共存和互操作应用程序、硬件、带宽可用性等方面的快速变化&#xff0…

Stable Diffusion中的Clip模型

基础介绍 Stable Diffusion 是一个文本到图像的生成模型&#xff0c;它能够根据用户输入的文本提示&#xff08;prompt&#xff09;生成相应的图像。在这个模型中&#xff0c;CLIP&#xff08;Contrastive Language-Image Pre-training&#xff09;模型扮演了一个关键的角色&a…

Biotin aniline,生物素苯胺,用于研究蛋白质结构和功能

您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;769933-15-5&#xff0c;Biotin aniline&#xff0c;生物素苯胺&#xff0c;Biotin-aniline&#xff0c;生物素-苯胺 一、基本信息 【产品简介】&#xff1a;Biotin aniline is composed of three parts: biotin, w…

个人或者小团队选择C语言还是c++?

个人或者小团队选择C语言还是c? 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C语言的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff0…

使用Python语言实现一个基于动态数组的序列队列

一、动态数组的实现 首先&#xff0c;我们需要创建一个DynamicArray类&#xff0c;该类将管理我们的动态数组。 动态数组能够动态地调整其大小&#xff0c;以容纳更多的元素。 目录 一、动态数组的实现 代码示例&#xff1a; 二、序列队列的实现 接下来&#xff0c;我…

代码随想录算法训练营第五天

● 自己看到题目的第一想法 242. 有效的字母异位词 方法&#xff1a; 方法一&#xff1a; 暴力法 1. 分别对s, t排序 2. 遍历s与t 判断s[i]!t[i] 返回 false 否则 返回true思路&#xff1a; 注意&#xff1a; 代码&#xff1a; bool cmp(char a, char b){return a<b;…

网站搭建的基本流程是什么?

网站搭建的基本流程是什么? 我们选择了白嫖雨云的二级域名 浏览器输入https://www.rainyun.com/z22_ 创建账号然后选择一个你喜欢的子域名我建议后缀选择ates.top的 选择自定义地址&#xff0c;类型选择cname 现在要选择记录值了&#xff0c;有a&#xff0c;aa&#xff0c;txt…

【Logback】Logback 的配置文件

目录 一、初始化配置文件 1、logback 配置文件的初始化顺序 2、logback 内部状态信息 二、配置文件的结构 1、logger 元素 2、root 元素 3、appender 元素 三、配置文件中的变量引用 1、如何定义一个变量 2、为变量设置默认值 3、变量的嵌套 In symbols one observe…