.so && .a

静态与动态库的区别

   文章选取的例子非常简单,上手容易,只是为了讲述静态与动态链接库的生成和链接过
    程,还有他们之间的区别。以下例子在 gcc 4.1.1 下顺利通过。


文件预览
文件目录树如下,如你所见,非常简单。

   1. libtest/ 
   2. |-- lt.c 
   3. |-- lt.h 
   4. `-- test.c 



代码
#lt.c

   1. /* lt.c
   2.  *
   3.  */ 
   4.  
   5. #include <stdio.h> 
   6.  
   7. void myprint(void) 
   8. { 
   9.   printf("Linux library test!/n"); 
  10. } 



# lt.h

   1. /* lt.h
   2.  * 
   3.  */ 
   4.  
   5. void myprint(void); 



#test.c

   1. /* test.c
   2.  *
   3.  */ 
   4.  
   5. #include "lt.h" 
   6.  
   7. int main(void) 
   8. { 
   9.   myprint(); 
  10.   return 0; 
  11. } 


先看静态库
首先做成静态库 liblt.a 。

   1. $ gcc -c lt.c -o lt.o 
   2. $ ar cqs liblt.a lt.o 




再者,链接,这里指定了静态库的位置,注意文件顺序不可乱序。

   1. $ gcc test.o liblt.a -o test 



这个时候再来看他的引用库情况。

   1. $ ldd test 
   2.         linux-gate.so.1 =>  (0xffffe000) 
   3.         libc.so.6 => /lib/libc.so.6 (0xb7e29000) 
   4.         /lib/ld-linux.so.2 (0xb7f6e000) 



动态库
做成动态库 liblt.so 。

   1. $ gcc -c lt.c -o lt.o 
   2. $ gcc -shared -Wall -fPIC lt.o -o liblt.so 



链接方法I,拷贝到系统库里再链接,让gcc自己查找

   1. $ sudo cp liblt.so /usr/lib 
   2. $ gcc -o test test.o -llt 


这里我们可以看到了 -llt 选项,-l[lib_name] 指定库名,他会主动搜索
lib[lib_name].so 。这个搜索的路径可以通过 gcc --print-search-dirs来查找。

链接方法II,手动指定库路径

   1. $ cc -o test test.o -llt -B /path/to/lib


这里的-B 选项就添加 /path/to/lib 到gcc搜索的路径之中。这样链接没有问题但是方法II
中手动链接好的程序在执行时候仍旧需要指定库路径(链接和执行是分开的)。需要添加系
统变量 LD_LIBRARY_PATH :

   1. $ export LD_LIBRARY_PATH=/path/to/lib 



这个时候再来检测一下test程序的库链接状况(方法I情况)

   1. $ ldd test 
   2.         linux-gate.so.1 =>  (0xffffe000) 
   3.         liblt.so => /usr/lib/liblt.so (0xb7f58000) 
   4.         libc.so.6 => /lib/libc.so.6 (0xb7e28000) 
   5.         /lib/ld-linux.so.2 (0xb7f6f000) 


恩,是不是比静态链接的程序多了一个 liblt.so ?恩,这就是静态与动态的最大区别,静
态情况下,他把库直接加载到程序里,而在动态链接的时候,他只是保留接口,将动态库与
程序代码独立。这样就可以提高代码的可复用度,和降低程序的耦合度。

文中是linux下 C++动态库 实现接口提供类导出的一个例子 注意其中使用函数返回基类指针的用法,因为Linux的动态链接库不能像MFC中那样直接导出类 一、介绍 如何使用dlopen API动态地加载C++函数和类,是Unix C++程序员经常碰到的问题。 事实上,情况偶尔有些复杂,需要一些解释。这正是写这篇mini HOWTO的缘由。 理解这篇文档的前提是对C/C++语言中dlopen API有基本的了解。 这篇HOWTO的维护链接是: http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/ 二、问题所在 有时你想在运行时加载一个库(并使用其中的函数),这在你为你的程序写一些插件或模块架构的时候经常发生。 在C语言中,加载一个库轻而易举(调用dlopen、dlsym和dlclose就够了),但对C++来说,情况稍微复杂。 动态加载一个C++库的困难一部分是因为C++的name mangling (译者注:也有人把它翻译为“名字毁坏”,我觉得还是不翻译好), 另一部分是因为dlopen API是用C语言实现的,因而没有提供一个合适的方式来装载类。 在解释如何装载C++库之前,最好再详细了解一下name mangling。 我推荐您了解一下它,即使您对它不感兴趣。因为这有助于您理解问题是如何产生的,如何才能解决它们。 1. Name Mangling 在每个C++程序(或库、目标文件)中, 所有非静态(non-static)函数在二进制文件中都是以“符号(symbol)”形式出现的。 这些符号都是唯一的字符串,从而把各个函数在程序、库、目标文件中区分开来。 在C中,符号名正是函数名:strcpy函数的符号名就是“strcpy”,等等。 这可能是因为两个非静态函数的名字一定各不相同的缘故。 而C++允许重载(不同的函数有相同的名字但不同的参数), 并且有很多C所没有的特性──比如类、成员函数、异常说明──几乎不可能直接用函数名作符号名。 为了解决这个问题,C++采用了所谓的name mangling。它把函数名和一些信息(如参数数量和大小)杂糅在一起, 改造成奇形怪状,只有编译器才懂的符号名。 例如,被mangle后的foo可能看起来像foo@4%6^,或者,符号名里头甚至不包括“foo”。 其中一个问题是,C++标准(目前是[ISO14882])并没有定义名字必须如何被mangle, 所以每个编译器都按自己的方式来进行name mangling。 有些编译器甚至在不同版本间更换mangling算法(尤其是g++ 2.x和3.x)。 即使您搞清楚了您的编译器到底怎么进行mangling的,从而可以用dlsym调用函数了, 但可能仅仅限于您手头的这个编译器而已,而无法在下一版编译器下工作。 三、类 使用dlopen API的另一个问题是,它只支持加载函数。 但在C++中,您可能要用到库中的一个类,而这需要创建该类的一个实例,这不容易做到。 四、解决方案 1. extern "C" C++有个特定的关键字用来声明采用C binding的函数: extern "C" 。 用 extern "C"声明的函数将使用函数名作符号名,就像C函数一样。 因此,只有非成员函数才能被声明为extern "C",并且不能被重载。 尽管限制多多,extern "C"函数还是非常有用,因为它们可以象C函数一样被dlopen动态加载。 冠以extern "C"限定符后,并不意味着函数中无法使用C++代码了, 相反,它仍然是一个完全的C++函数,可以使用任何C++特性和各种类型的参数。
<think>嗯,用户现在问的是关于“cmake .. && make”这段代码是什么意思。之前他们遇到了权限被拒绝的问题,可能是在尝试运行FunASR项目的时候出的错。现在他们可能已经按照我之前的建议去编译项目,但需要更深入理解这两条命令的作用。 首先,我需要确定用户对编译过程的了解程度。他们可能刚开始接触C/C++项目,不太清楚构建工具的使用。所以需要从基础解释,但不要过于技术化,保持易懂。 “cmake ..”这条命令,用户可能知道cmake是用于生成构建文件的工具,但需要解释为什么要在build目录下执行,以及“..”的作用。这里需要说明CMake的工作流程,比如创建build目录是为了保持源码干净,避免生成的文件混在一起。而“..”指的是CMakeLists.txt所在的父目录,也就是项目的根目录。 然后“make”是实际编译的命令,调用Makefile中的指令来编译源代码。要解释make是如何根据生成的Makefile来调用编译器如g++或clang,将源代码转换为可执行文件的过程。 接下来是“&&”的作用,这个符号在shell中表示前一个命令成功后才执行后面的命令。这样用户可以理解为什么两条命令要连在一起使用,确保只有在CMake成功生成Makefile之后才会进行编译,避免错误。 用户可能的问题还包括:为什么需要分开cmake和make?为什么要在build目录里执行?如果cmake失败,make还会执行吗?这时候需要解释分阶段构建的好处,比如分离配置和编译步骤,便于清理和管理构建产物。同时,使用&&可以避免在配置失败后继续编译,减少错误。 还要考虑用户可能的后续步骤,比如是否还有其他构建步骤,或者如何安装编译后的文件。可能需要提到“make install”或者其他目标,但根据当前问题,可能不需要深入,除非用户继续提问。 另外,用户之前遇到权限问题,可能现在是在学习如何正确编译项目,所以需要强调正确使用构建流程的重要性,避免直接运行源码文件导致的权限错误。可能可以关联之前的解决方案,提醒用户确保生成的可执行文件路径正确,并且有执行权限。 最后,总结这两条命令的作用,确保用户明白这是C/C++项目标准的构建流程,帮助他们更好地理解项目编译的过程,减少未来遇到类似问题的困惑。</think>`cmake .. && make` 是 C/C++ 项目中常用的两条命令,用于 **编译和构建项目**。下面分步解释其含义和作用: --- ### **1. `cmake ..`** - **作用**: 调用 CMake 工具,根据项目中的 `CMakeLists.txt` 配置文件,生成适合当前系统的**构建文件**(如 Makefile、Visual Studio 项目文件等)。 - **关键细节**: - `..` 表示 CMake 的配置文件(`CMakeLists.txt`)位于**当前目录的上一级目录**。 通常建议在项目根目录下新建一个 `build` 子目录,并在其中运行 `cmake ..`,以保持源码目录的整洁(避免编译中间文件污染源码)。 - CMake 会自动检测系统环境(如编译器类型、依赖库路径等),并生成对应的构建规则。 - **示例**: ```bash mkdir build # 创建构建目录(通常首次编译时需要) cd build # 进入构建目录 cmake .. # 根据上级目录的 CMakeLists.txt 生成 Makefile ``` --- ### **2. `&&`** - **作用**: 在 Linux/Unix 的 Shell 中,`&&` 表示**逻辑“与”操作**。 只有前一条命令(`cmake ..`)**执行成功**(返回退出码 0),才会执行后一条命令(`make`)。 如果 `cmake ..` 失败,整个流程会终止,避免无效编译。 --- ### **3. `make`** - **作用**: 调用 `make` 工具,根据 CMake 生成的 `Makefile` 文件,**编译源代码并生成可执行文件或库**。 - `make` 会读取 `Makefile` 中的编译规则,自动处理源代码之间的依赖关系。 - 默认情况下,它会编译整个项目,生成目标文件(如二进制程序 `.exe`、静态库 `.a`、动态库 `.so` 等)。 - **常用附加参数**: - `make -j4`:启用多线程编译(`4` 表示线程数,可加快编译速度)。 - `make clean`:清理编译生成的文件(需重新编译时使用)。 - `make install`:将编译好的文件安装到系统路径(需权限,通常配合 `sudo` 使用)。 --- ### **完整流程示例** 假设项目结构如下: ``` project_root/ ├── CMakeLists.txt # CMake 配置文件 ├── src/ # 源代码目录 └── build/ # 构建目录(需手动创建) ``` 编译步骤: ```bash # 进入项目根目录 cd /path/to/project_root # 创建并进入构建目录(首次编译时需要) mkdir build && cd build # 生成 Makefile(根据上级目录的 CMakeLists.txt) cmake .. # 编译项目(使用 4 个线程加速) make -j4 ``` --- ### **总结** - `cmake ..`:**配置项目**,生成构建规则文件(如 `Makefile`)。 - `make`:**执行编译**,根据规则文件生成最终的可执行程序或库。 - `&&`:确保只有 `cmake` 成功后才执行 `make`,避免错误累积。 这是 C/C++ 项目的标准编译流程,适用于绝大多数使用 CMake 管理的开源项目(如 OpenCV、FFmpeg 等)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值