Linux基础IO(七)动静态库的制作与使用

news2026/3/19 15:58:11
目录一、回顾动静态链接二、什么是库库的本质三、库的制作静态库的封装法一:法二:法三:动态库的封装法一:法二:法三:四、总结一、回顾动静态链接mytest 是我们已经编译运行成功的一个C语言程序ldd 命令的作用是告诉你“运行这个程序它需要依赖哪些库文件以及这些库文件在系统的哪个路径下”。libc.so.6 是C 语言标准库。 指向实际位置。/lib64/libc.so.6 是 CentOS 系统下真实的库文件路径。系统告诉我们运行 mytest 必须去 /lib64/ 目录下找 C 标准库。我们看到的 libc.so.6 其实是一个软链接(快捷方式)。它真正指向的是具体的版本文件libc-2.17.so。1. 真实库文件名libc-2.17.so前缀 libLinux 下所有库文件的固定开头代表 library 库。核心名 c代表 C 语言标准库C Standard Library。版本号 -2.17这是库的具体版本号2 是主版本17 是次版本。后缀 .so代表 Shared Object动态共享库)也就是我们常说的动态库。2. 软链接名libc.so.6它是为了版本兼容而存在的“别名”。这里的 .6 是 ABI 版本号Application Binary Interface它保证了不同版本的库在二进制层面上的兼容性。程序永远依赖这个别名而不是带具体版本号的文件名这样系统升级库时只需要修改软链接指向程序就能无缝使用新版本。所以 libc-2.17.so 去掉前缀 lib 和后缀 .so剩下的 c-2.17 才是核心标识。而 libc.so.6 是它的兼容别名方便程序调用和系统升级。我们还可以是同 file 命令来查看文件是动态链接还是静态链接dynamically linked (uses shared libs) 的意思是程序是动态链接的运行时需要加载外部共享库(比如 libc.so.6)。C/C在使用gcc和g编译链接的时候默认使用动态链接。如果程序要使用静态链接的话加 -static并且在Linux中.so是动态库文件.a是静态库文件二、什么是库本质上说库是一种可执行代码的二进制形式可以被操作系统载入内存中执行这张图显示的是Ubuntu 和 CentOS 下 C/C 动静态库的位置对比:1. 先分清动态库 vs 静态库动态库 .so 结尾Shared Object运行时才加载程序体积小依赖系统库。静态库 .a 结尾Archive编译时直接打包进程序程序体积大运行时不依赖外部库。2. Ubuntu 与 CentOS 的库路径对比C 语言库libc类型Ubuntu路径CentOS路径说明动态库/lib/x86_64-linux-gnu/libc-2.31.so/lib64/libc-2.17.so真实动态库文件带版本号静态库/lib/x86_64-linux-gnu/libc.a/lib64/libc.a静态库用于静态链接C 语言库libstdc类型Ubuntu 路径CentOS 路径说明动态库/usr/lib/gcc/x86_64-linux-gnu/9/libstdc.so/lib64/libstdc.so.6动态库/软链接静态库/usr/lib/gcc/x86_64-linux-gnu/9/libstdc.a/usr/lib/gcc/x86_64-redhat-linux/4.8.2/libstdc.a静态库“Ubuntu 和 CentOS 下C/C 动静态库的存放位置、命名规则、软链接结构完全一致只是目录不同。”我们现在有这几个文件:我们先一次性编译所有源码文件直接生成可执行文件编译器会把 main.c主程序、mystdio.c 、mystring.c 功能实现全部编译并链接成一个独立可执行文件 target 。直接执行 ./target 即可正常运行输出 write file done! 和 这是我自己的strlen函数! 。我们还可以这样:gcc -c 源文件.c -o 目标文件.o : -c 参数的作用是只编译不链接把 .c 源码编译成 .o 目标文件二进制中间产物。每个 .c 对应一个 .o但还没完成最终链接不能直接运行。gcc -o target main.o mystdio.o mystring.o : 这条命令是把多个 .o 目标文件链接成一个可执行文件 target 。编译器会遍历所有 .o解析符号引用比如 main.o 里调用的 mystrlen 函数)在其他 .o 里找到实现合并成一个完整的可执行程序。生成的 target 和直接编译源码的效果完全一致体积 13184 字节运行时零依赖直接 ./target 就能输出结果。所以从上面两种方法我们可以得出 : 形成可执行程序时第一种方法是直接把源文件一次性编译成可执行第二种方法是把源文件先编译成.o文件再把.o文件链接成可执行程序第二种方法也是编译链接文件的最佳实践方法。这个 Makefile 就是自动把所有 .c 编译成 .o再把所有 .o 链接成可执行程序 target最后用 make clean 一键清理。那有一个问题 : 假设有多个.c 源文件,我们把其中一个源文件编译成.o文件时和其他的源文件有关系吗? 答案是没有关系 , 这些由.c文件编译成的.o文件只有在链接的时候才有依赖关系,还要注意的是只有 .c 文件才会被编译成 .o 文件.h 文件不会因为.c 文件是实现文件里面写的是函数、变量的具体代码逻辑编译器会把它翻译成机器码生成 .o 文件。而 .h 文件是头文件本质是接口声明/宏定义/类型声明它不会被单独编译成 .o。头文件是被 .c 文件 #include 进去和 .c 一起参与编译。库的本质现在我们知道在编译阶段时当我们执行 gcc -c main.c 或 gcc -c mystring.c 时编译器会把 .c 源码翻译成 .o 文件。并且是单文件独立运作的。编译 mystring.o 时不需要 main.c 的存在。编译 main.o 时也不需要 mystring.c 的存在。此时它们之间没有任何依赖关系谁断了都不影响谁生成。但是在链接时当执行 gcc main.o mystring.o mystdio.o -o target 时链接器Linker会把所有 .o 文件打包合并统一组装。并且链接器还会负责把 main 里调用的函数比如 my_strlen)在 mystring.o 里找到并连起来。如果少一个 .o链接就会报错。这时在链接阶段才会建立依赖关系。既然链接前的 .o 文件是独立的那么如果我们把 .o 文件打包压缩就变成了 静态库( .a ) 。以后用的时候直接把我们打包好的静态库 libmystring.a 和 main.o 链接效果和刚才直接链接 .o 是一样的。所以库的本质就是 . o 文件而且是已经打包好的 .o 文件的集合。三、库的制作静态库的封装其实封装静态库有多种方法我们最开始是把功能代码写在 .c 文件里然后用 gcc -c 把这些 .c 编译成 .o 目标文件这些 .o 里面是真正的函数实现但还不能直接给别人用于是我们用 ar 命令把多个 .o 打包成 .a 静态库文件这一步就是封装库。同时我们把函数声明、宏定义写在 .h 头文件里这个头文件就是库的使用说明书没有它别人就不知道怎么调用库里的函数。所以给别人提供库时必须同时给头文件和库文件缺一不可。接下来就有多种使用这个库的方法法一:第一种方法就是将把库和头文件复制到系统默认目录首先我们先将 .o 文件进行打包封装:首先我们先将 .c 源文件编译成 .o 文件这些 .o 文件是独立的只包含各自 .c 文件的机器码互相之间没有依赖关系。第二步用 ar 命令打包成静态库( .a )ar -rc libmystdio.a *.oar 命令是静态库的核心制作命令ar ArchiverLinux 下用于打包目标文件的工具。-r replace —— 如果库已存在就替换其中的旧 .o 文件如果不存在就新建。-c create —— 强制创建库文件即使已存在也会覆盖。libmystdio.a 我们定义生成的静态库文件名Linux 静态库必须以 lib 开头.a 结尾。*.o 通配符把当前目录下所有 .o 文件mystdio.o 、mystring.o都打包进库。还要注意的是一定不能将 main 主函数所在的文件打包进静态库原因很简单静态库是“功能模块集合”静态库( .a )是可复用的功能模块比如我们封装的 mystdio、mystring是提供函数给别人调用。而 main 函数是程序的唯一入口点程序从 main 开始执行它是“调用别人的人”不是“被别人调用的人”。如果把 main.o 也打包进库别人用你的库时会得到两个 main 入口从而报错。那到这里就完了吗? 并没有。为什么会报错? 原因是这个 gcc -o target main.o 命令只告诉链接器“把 main.o 变成可执行文件”但完全没提 libmystdio.a 这个库。在链接器的视角只看到 main.o 里调用了 myfopen但 main.o 里没有这个函数的实现。也没收到任何指令要去我们封装的 libmystdio.a 静态库里找。并且 gcc 只去系统级的 /lib 、/usr/lib 、/lib64 、/usr/lib64 等目录找库。所以这里只能报错“我找不到这个函数的实现”。解决方法:这次我们成功链接并且 target 生成并成功运行。gcc -o target main.o -lmystdio -L./ // -lmystdio 告诉链接器“我要用 libmystdio.a 这个库。” // -L./ 告诉链接器“别去系统默认目录找了去我当前目录 ./ 找。”这个命令的含义是首先 gcc 编译器发起构建命令-o target 指定输出文件名生成的可执行文件叫 targetmain.o 是你的主程序目标代码输入文件-L./ 表示指定库路径意思是去当前目录找库文件。-lmystdio 表示对指定库名进行链接-l 是link(链接)mystdio 就是库名gcc 会在当前路径下自动搜索 libmystdio.a 或 libmystdio.so 。所以我们也可以写出静态库制作的Makefile这个 Makefile 是用来自动化构建静态库 libmystdio.a 它的核心逻辑是先把所有 .c 源文件编译成 .o 目标文件再将这些 .o 文件打包成静态库同时提供一键清理功能。具体来说第一行定义了要生成的静态库 libmystdio.a 依赖 mystdio.o 和 mystring.o下方的 ar -rc $ $^ 命令会把这两个依赖的 .o 文件打包成库其中 $ 代表目标库名$^ 代表所有依赖文件。接下来的 %.o:%.c 是通用模式规则意思是所有 .o 文件都依赖同名的 .c 文件下方的 gcc -c $ 会自动把对应的 .c 文件编译成 .o $ 代表当前依赖的 .c 文件。最后是清理规则.PHONY: clean 声明 clean 是伪目标避免和真实文件冲突clean 目标下的 rm -f *.o *.a 会删除所有生成的 .o 目标文件和 .a 静态库文件实现一键清理编译产物的效果。整个脚本运行后只要执行 make 就能自动完成从源码到静态库的构建流程执行 make clean 则可以快速恢复干净的开发环境是 Linux 下模块化开发和静态库封装的标准自动化方案。我们在制作库时以制作者的视角除了给用户提供库文件 .a 或 .so )还应给用户提供对应的头文件 .h )两者缺一不可。库文件比如 libmystdio.a 本质是把多个 .c 源文件的实现代码编译、打包后得到的二进制包里面存放着函数、变量的具体实现逻辑是程序运行时真正执行的机器码。而头文件 .h 则是库的“接口说明书”它只包含函数声明、宏定义、类型别名等内容不包含任何可执行代码作用是告诉使用者“这个库里有哪些函数可以调用、函数的参数和返回值是什么类型”让编译器在编译使用者的 main.c 时能完成语法检查和符号引用确保后续链接阶段能顺利找到库中的实现代码。简单来说库文件是“零件箱”装着所有功能的实现代码头文件是“零件说明书”告诉别人怎么使用这些零件。没有头文件使用者就不知道库中函数的调用格式编译器会报“未声明的函数”错误没有库文件链接器就找不到函数的具体实现会报“未定义的引用”错误。只有同时提供这两个文件别人才能像使用系统标准库一样通过 #include mystdio.h 引入接口声明再用 -lmystdio 链接你的库最终写出完整的可执行程序。我们可以在Makefile中对头文件进行相关处理 : 新增的 output 目标是整个流程的关键它先声明自己是伪目标然后依次执行四条命令第一条 mkdir -p mylib/include 强制创建 mylib/include 目录专门用来存放头文件第二条 mkdir -p mylib/lib 强制创建 mylib/lib 目录专门用来存放库文件第三条 cp *.h mylib/include 将当前目录下所有的头文件复制到 include 目录第四条 cp *.a mylib/lib 将生成的静态库文件复制到 lib 目录。执行 make output 后就会在当前目录生成一个 mylib 文件夹里面结构清晰include 放着接口声明lib 放着二进制实现使用者只需要把这个 mylib 文件夹拷贝到自己的工程里通过 -I 指定头文件路径、-L 指定库路径就能直接链接使用这就是工业级库的标准交付方式实现了接口和实现的严格分离方便分发和维护。我们只要执行 make output整个流程依然是先编译 .c 为 .o再打包成 libmystdio.a 库文件。然后在当前目录下创建 mylib 文件夹。在 mylib 里面再创建 include 和 lib 两个子文件夹。把当前目录下所有 .h 头文件复制到 mylib/include 里。把当前目录下所有 .a 静态库文件复制到 mylib/lib 里。mkdir -p 里的 -p 参数很关键它的作用是“如果目录已经存在就不报错直接跳过”所以反复执行 make output 也不会出问题只会覆盖旧文件。那么到现在为止作为一个库的制作者整个静态库就创建成功了但是作为用户任务还没有完成这个库又怎么使用呢?这一步就是把库“安装到系统目录”让用户可以像用系统标准库一样直接使用你的库。从上图可以看到我们把自己的头文件和静态库分别拷贝到了系统默认路径头文件mystdio.h和mystring.h被拷贝到 /usr/include/这是系统默认的头文件搜索目录用户写代码时直接 #includemystdio.h 就能找到接口声明。其次静态库 libmystdio.a 被拷贝到 /lib64/这是CentOS 系统默认的库文件搜索目录用户链接时直接用 -lmystdio 就能找到库的实现不需要再加 -L 指定库路径。这样做的好处是用户拿到你的库后完全不用关心库和头文件在哪就像使用 #include stdio.h 和 -lc 一样直接写代码、链接就能跑体验和系统自带的库一模一样。不过要注意因为 /usr/include 和 /lib64 是系统级目录普通用户没有写入权限所以必须用 sudo 提升权限才能执行拷贝操作这也是图里命令前面加 sudo 的原因。现在我们的库已经制作完成并且也已拷贝到系统的默认路径下为啥 gcc main.c 还会报错? 因为我们的库属于第三方库和系统自带的 libc 、libstdc 本质一样只是需要显式指定 -lmystdioh. 告诉编译器“我要用这个库”就能和系统库的使用体验完全一致了。gcc main.c -o main -I ./mylib/include/ -L ./mylib/lib/ -lmystdio如果我们用 sudo rm 把 /usr/include/mystdio.h 和 /lib64/libmystdio.a 给删掉。编译器去系统默认目录( /usr/include )找头文件结果发现这里被你删空了所以它直接报错停止编译根本没走到链接阶段。同时也间接验证了我们之前的知识点1. 如果没有头文件连编译都过不了。这证明了头文件是接口声明必不可少。2. 验证了系统默认路径的作用你之前把库安装到系统目录就是为了让编译器默认去 /usr/include 找头文件默认去 /lib64 找库文件。现在你把它们删了编译器就罢工了。3. 验证了 -l 参数的作用虽然你加了 -lmystdio 但因为头文件都没了编译器根本不会走到链接环节。先有头文件(编译)后有库(链接)。法二:除了将库搬进系统默认目录下使用库外还有没有其他方法也能正常使用库?答案是有的我们紧接着上面的内容当我们用 sudo rm 把 /usr/include/mystdio.h 和 /lib64/libmystdio.a 从系统默认目录删掉后还可以使用下面的命令重新找到库文件和头文件来正常使用库。gcc main.c -o main -I./mylib/include/ -L./mylib/lib/ -lmystdio参数含义为什么必须加-I./mylib/include/I Include指定头文件路径 因为你没把 mystdio.h 装到系统默认路径编译器找不到头文件所以必须手动告诉它“去 ./mylib/include 找头文件”-L./mylib/lib/L Library指定库文件路径 同理编译器默认不去你当前目录找库必须手动告诉它“去 ./mylib/lib 找 libmystdio.a ”-lmystdiol link链接库名告诉编译器你要链接的库名字是 mystdio 编译器会自动搜索 libmystdio.a 。如果只加 -I (只有头文件路径 编译阶段虽然能过因为 -I 告诉编译器去 ./mylib/include 找到了 mystdio.h函数声明没问题。但是链接阶段必报错 编译器只看到你调用了myfopen, mystrlen但你没告诉它去哪找实现代码(没加 -L 和 -l )所以会报 undefined reference (未定义的引用。只加 -I 和 -L(有头文件路径 库路径但没 -l )编译阶段能过因为头文件找到了。链接阶段还是报错因为你告诉了编译器“去 ./mylib/lib 这个文件夹找”但没告诉它“找哪个库”链接器不知道你要链接 libmystdio.a依然会报 undefined reference。这种方法不改动系统只是把库和头文件放在自己项目里的 mylib 目录编译时用 -I 指定头文件路径用 -L 指定库所在目录再用 -l 链接库名虽然命令长一点但不需要权限、不污染系统、更安全规范也是实际项目里最标准的做法。法三:第三种方法是使用软链接把库“映射”到系统目录:这条命令用 sudo 权限创建了一个软链接快捷方式-s 代表软链接意思是在系统目录 /lib64 里生成一个指向你项目中真实库文件 libmystdio.a 的快捷方式名字也叫 libmystdio.a。这样编译器就会把它当成系统库来处理后续编译时只需要 -lmystdio 就能找到库的实现而且这个快捷方式不占用实际磁盘空间只是一个指向原文件的指针。这条命令用来查看 /lib64 目录下 libmystdio.a 的详细信息-l 参数会显示文件类型、权限、大小和指向路径。从输出可以看到它是一个软链接开头 l 标识并且明确指向了你项目里的 libmystdio.a证明软链接创建成功。这条命令用 sudo 权限把当前项目 mylib/include 目录下所有 .h 头文件复制到系统默认头文件目录 /usr/include 这样编译器在编译 main.c 时能直接通过 #include mystdio.h 找到函数声明不需要再加 -I 指定头文件路径。这条命令是编译主程序并链接你的库-o main 指定生成可执行文件 main-lmystdio 告诉编译器链接 libmystdio.a。因为头文件已经在 /usr/include 、库文件通过软链接映射到 /lib64都在系统默认路径里所以编译器能自动找到并完成编译链接不需要额外的 -I 和 -L。总结一下就是先通过软链接把项目里的库“映射”到系统目录再把头文件拷贝到系统目录之后就可以像使用系统库一样只用 -lmystdio 完成编译既方便又能保持库文件在项目中可控是一种高效且安全的开发方式。动态库的封装不管是封装静态库还是动态库核心逻辑都是一样的都要先把 .c 源文件编译成 .o 目标文件这些 .o 里存放着函数的具体实现代码。都要配套 .h 头文件头文件里写函数声明、宏定义、类型别名是库的“接口说明书”没有它别人就不知道怎么调用库里的函数。交付给用户时都必须同时提供头文件( .h )和库文件( .a 或 .so )缺一不可。都可以通过 Makefile 自动化构建比如用模式规则自动编译 .c 为 .o用 output 目标把库和头文件整理成 mylib/include (放头文件)和 mylib/lib (放库文件的标准交付目录。我们现在命令行封装并打包然后再在 Makefile 中修改:gcc -fPIC -c *.c 是把所有 .c 源文件编译成满足动态库要求的 .o 目标文件-fPIC 是 gcc 的编译选项全称是 Position-Independent Code位置无关代码它的作用是让生成的 .o 目标文件里的机器码不依赖固定内存地址这样动态库加载到内存任意位置时都能正常执行是生成动态库的硬性要求——静态库不需要这个参数因为静态库会在编译时完整拷贝到可执行文件地址是固定的。再执行gcc -shared -o libmystdio.so *.o 把这些 .o 打包成动态库 libmystdio.so 两个参数配合起来就完成了动态库的核心构建流程。-shared 是 gcc 的链接选项作用是告诉编译器将多个 .o 目标文件打包生成动态共享库 .so 文件而不是生成普通的可执行文件。这个选项会让编译器保留所有符号引用不把代码嵌入可执行文件最终生成的 .so 文件可以被多个程序在运行时共享加载实现内存复用。静态库则是用 ar -rc 命令打包不需要这个选项。当我们执行 make output 后现在的目录里应该是被打包进了 libmystdio.so 库里的 .o 文件mylib/ 目录mylib/include/ 放着你的 .h 头文件接口声明)mylib/lib/ 放着 libmystdio.so(动态库函数实现。下一步就是编译 main.c 并链接你的动态库命令和静态库类似但要注意动态库的运行时依赖-I./mylib/include 告诉编译器去哪找头文件 mystdio.h-L./mylib/lib 告诉编译器去哪找动态库 libmystdio.so-lmystdio 告诉编译器要链接 libmystdio.so 这个动态库又有问题了为什么我们运行这个程序时不成功并且库显示 not found 因为我们编译时虽然用了 -L ./mylib/lib但这个参数只在编译链接时生效告诉编译器“去这个目录找库完成链接”并没有告诉运行时的系统加载器“去这个目录找库”。系统加载器在运行 ./main时只会去以下几个地方找动态库1. 系统默认库路径( /lib64、/usr/lib64 等)2. 环境变量 LD_LIBRARY_PATH 里的路径我们的 libmystdio.so 不在这些地方所以系统加载器找不到它就显示 not found 。那之前静态库为什么直接 ./main 就能跑因为静态库 .a 在编译链接阶段会把库里面的函数实现代码完整拷贝到最终的可执行文件里我们写 gcc main.c -o main -lmystdio 时编译器会把 libmystdio.a 里的myfopen 、mystrlen 等函数实现直接“粘”进 main 这个可执行文件里。生成的 main 是一个完全独立的程序它不再依赖任何外部库文件所有代码都在自己内部。所以你直接 ./main 就能跑系统根本不需要去外面找任何库自然也不会出现“找不到库”的报错。动态库为什么不行动态库.so在编译链接阶段只做了一件事记录依赖关系不拷贝任何代码我们写 gcc main.c -o main -lmystdio 时编译器只是确认“ main 里用到的函数在 libmystdio.so 里”然后在 main 里留个“调用入口”告诉系统“运行时去加载 libmystdio.so ”。生成的 main 是一个“空壳程序”它自己没有函数实现必须在运行时让系统加载器找到 libmystdio.so才能把代码读进来执行。所以你直接 ./main 时系统加载器找不到 libmystdio.so 就会报错。静态库编译时把代码拷进程序程序“吃饱了”运行时自给自足动态库编译时只留个“欠条”程序“饿肚子”运行时必须找系统要饭找库。解决方法:法一:sudo cp mylib/lib/libmystdio.so /lib64这一步是把我们自己的动态库 libmystdio.so 拷贝到了系统默认的库目录 /lib64 里。所以系统加载器 ld-linux-x86-64.so.2 在运行程序时会在系统默认库路径比如 /lib64 、/usr/lib64查找动态库我们把 libmystdio.so 放到了 /lib64正好是系统默认路径之一所以运行 ./main 时系统加载器自动去 /lib64 找到了 libmystdio.so 成功加载库代码。ldd main 也显示 libmystdio.so /lib64/libmystdio.so证明库依赖已经被正确解析。程序最终输出“我自己的string len 函数”说明整个动态链接流程完全打通。法二:第二种方法是创建软链接sudo ln -s 源路径 /lib64/libmystdio.so这是在系统库目录 /lib64 里创建一个软链接符号链接相当于给你的真实动态库 libmystdio.so 做了一个“快捷方式”。从 ll 输出可以看到/lib64/libmystdio.so - 你的项目路径 文件类型以 l 开头明确是软链接不占用实际磁盘空间。系统加载器会把这个软链接当成真实库文件自动去系统默认路径查找效果和直接拷贝到系统目录完全一致。法三:第三种方法是动态库的临时环境变量法:LD_LIBRARY_PATH 是 Linux 系统中动态链接器的环境变量专门用来告诉系统“除了默认路径外还要去哪些目录找动态库”。执行的 export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/你的库路径是把自己的动态库目录追加到原有环境变量中不会覆盖系统原有的路径配置保证系统库如 libc.so.6仍能正常找到。从 echo $LD_LIBRARY_PATH 输出可以看到你的库路径已经被成功加入环境变量列表。需要注意的是 LD_LIBRARY_PATH 是“找库的目录列表”只需要告诉系统“去哪个目录找”系统会自动在目录里匹配库名不需要也不能把具体库文件名加进去。语法上 LD_LIBRARY_PATH 只接受目录路径不能写具体文件名否则系统会把它当成目录名去查找反而找不到库。运行时系统会去这个目录里找和你编译时 -lmystdio 对应的 libmystdio.so 文件自动匹配。ldd main 输出显示 libmystdio.so /你的项目路径/libmystdio.so说明系统加载器已经能通过 LD_LIBRARY_PATH 找到你的动态库。直接 ./main 即可运行输出“我自己的string len 函数”证明临时环境变量法下动态库加载和函数调用完全正常。四、总结本文详细介绍了Linux系统中动静态库的创建与使用方法。静态库.a在编译时将代码嵌入程序运行时无需依赖动态库.so在运行时加载实现内存共享。制作库需将.c文件编译为.o目标文件静态库用ar命令打包动态库需加-fPIC和-shared选项。使用库时需同时提供头文件和库文件可通过三种方式1拷贝到系统目录2指定路径-I/-L3创建软链接。动态库运行时还需配置LD_LIBRARY_PATH环境变量或安装到系统目录。文章通过具体命令演示了库的封装、安装和使用全过程。谢谢大家的观看!

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…