有没有可能通过静态链接标准库来创建一个静态库?
不可能,因为你不能将任何东西链接到静态库中。你只能将其链接到由链接器生成的文件中。否则,在其生产过程中不涉及任何链接。
链接器可以生成两种类型的文件:程序和共享库。程序和共享库类似:它们都由可执行代码组成,程序加载器可以将其加载到进程中。它们与静态库不相似。静态库是由归档程序ar
生成的一组对象文件 - 一个存档文件,链接器可以从中提取需要完成程序或共享库链接的文件。
像这样的命令:
g++ -c library.cpp -static-libstdc++ -o library.o
只编译library.cpp
成library.o
,并忽略链接选项-static-libstdc++
,
因为-c
的意思是仅编译,不链接
您真正的问题在于您稍后的评论中才被揭示1:
问题在于我正在编写一个C ++代码包装器以便在C中使用它,
在C中,我不能使用标准C ++库。唯一的方法是在库中包含我从标准库中使用的函数。
现在,由于共享库是链接器可以生成的内容,所以您可以将libstdc ++
静态链接到共享库中。
因此,您可以将C包装器库制作成共享库,而不是静态库。
看起来您已经知道如何在C API中包装C ++代码。因此,让我们编写这样的C包装器,将输入缓冲区中的前N个字符反转到输出缓冲区中,
使用标准C ++库完成所有工作:
reverse.h
#ifndef REVERSE_H
#define REVERSE_H
#ifdef __cplusplus
extern "C" {
#endif
void reverse(char const * in, char * out, unsigned len);
#ifdef __cplusplus
}
#endif
reverse.cpp
#include "reverse.h"
#include <string>
#include <algorithm>
extern "C" {
void reverse(char const * in, char * out, unsigned len)
{
std::string s{in,len};
std::reverse(s.begin(),s.end());
std::copy(s.begin(),s.end(),out);
}
}
接下来是调用reverse
函数的C程序:
main.c
#include <stdio.h>
#include <reverse.h>
int main()
{
char in[] = "dlrow olleH";
reverse(in,in,sizeof(in) - 1);
puts(in);
return 0;
}
现在我们将构建共享的包装库。
编译我们的一个库源文件:
$ g++ -fPIC -Wall -Wextra -std=c++11 -c reverse.cpp
请注意 -fPIC
。我们将链接到共享库中的所有目标文件都必须是位置无关代码。由于我们只编译一个源文件 -c reverse.cpp
,因此可以跳过 -o
选项并接受默认值 -o reverse.o
。
然后链接我们的共享库:
$ g++ -shared -o libreverse.so reverse.o -static-libstdc++
现在共享库是
./libreverse.so
,它没有运行时依赖于
libstdc++
。
$ ldd libreverse.so
linux-vdso.so.1 => (0x00007ffca98c9000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa178862000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa178498000)
/lib64/ld-linux-x86-64.so.2 (0x000055d17658c000)
接下来编译我们的C程序源代码文件:
$ gcc -Wall -Wextra -I. -c main.c
最后,将程序与libreverse
链接:
$ gcc -o prog main.o -L. -lreverse
这个程序
./prog
在运行时依赖共享库
libreverse.so
。
因为它不是静态库,所以无法静态链接
libreverse.so
。
但是,
libreverse.so
已经使用
libstdc++.a
静态链接,并且具有 C API。
如果
libreverse.so
是一个重要的库,我们现在需要做的是将其安装到运行时加载器的标准搜索目录之一,以便程序可以在运行时自动加载它。我们可以这样做:
$ sudo cp libreverse.so /usr/local/lib/
$ sudo ldconfig
但是由于 libreverse.so
只是一个玩具库,我们不会为它修改我们的系统。相反,我们只需像这样运行 ./prog
:
$ export LD_LIBRARY_PATH=.
$ ./prog
Hello world
所以,您可以使用C API和C++内部制作包装库,并通过使包装库成为共享库并静态链接
libstdc ++
来实现。
但是为什么要这样做呢?
似乎您想要这样做是因为您认为“在C中我无法使用标准的C ++库”。
这是一个误解。以下是如何使用
静态包装库构建相同程序的方法。
首先创建您的静态包装库。
$ rm libreverse.so
$ g++ -Wall -Wextra -std=c++11 -c reverse.cpp
$ ar rcs libreverse.a reverse.o
然后编译您的C源代码:
$ gcc -Wall -Wextra -I. -c main.c
在这一步,您有一个对象文件
main.o
,一个静态库
libreverse.a
,其中只包含
reverse.o
,要创建
prog
,您只需要将
main.o
和
libreverse.a(reverse.o)
与标准C库和标准C++库链接在一起。没有关于
C不允许您这样做的问题。当您编译
main.c
时,就已经完成了C语言的部分。这些对象文件和库将由系统链接器链接,它不会知道或关心它们是从哪种语言编译而来的。
因此,您可以通过g++
调用链接器,如下所示:
$ g++ -o prog main.o -L. -lreverse
现在你有一个能够做到这一点的程序:
$ ./prog
Hello world
或者你可以通过gcc
调用链接器,例如:
$ gcc -o prog main.o -L. -lreverse -lstdc++
这只是相同的链接,具有相同的结果:
$ ./prog
Hello world
正如您所见,使用
g++
链接与使用
gcc
链接的唯一区别在于,
g++
会自动将标准C库和标准C++库添加到链接器的输入中,而
gcc
不会添加C++库,因此您需要手动添加。
实际上,如果您的计算机上安装了GNU Fortran,您也可以使用它进行相同的链接:
$ $ gfortran -o prog main.o -L. -lreverse -lstdc++
$ ./prog
Hello world
尽管
prog
中没有使用 Fortran,但这并不影响。
C 不会阻止您将
libstdc++
与任何东西链接起来。您可能希望将
libstdc++
静态链接到具有 C API 的库中的一个合理但不太可能的理性动机是,您希望其他人能够在没有
libstdc++
或没有与您的
ABI 兼容的系统上链接此库中的程序。如果这是您的动机,请像上面所示将您的库设置为共享库。否则,只需将
libstdc++
与任何依赖它的程序链接即可。
[1] 在 Stackoverflow 上,请始终在问题中陈述您的
真实问题。请参见
The XY Problem。