- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
编写库时最重要的规则和最佳实践之一是将所有符号放在库到特定于库的命名空间。由于 namespace
关键字,C++ 使这变得容易。在C 通常的方法是在标识符前加上一些特定于库的前缀。
C 标准的规则对这些进行了一些限制(为了安全编译):C 编译器可能只查看第一个标识符的 8 个字符,因此 foobar2k_eggs
和 foobar2k_spam
可能被解释为相同标识符有效——然而每个现代编译器都允许任意长的标识符,所以在我们这个时代(21 世纪)我们不必为此烦恼。
但是,如果您面对一些无法更改符号名称/标识符的库怎么办?也许你有只有静态二进制文件和 header 或者不想,或者不允许自己调整和重新编译。
最佳答案
至少对于静态 库,您可以很方便地解决它。
考虑那些库的标题 foo 和 bar。为了本教程,我还将为您提供源文件
int spam(void);
double eggs(void);
int the_spams;
double the_eggs;
int spam()
{
return the_spams++;
}
double eggs()
{
return the_eggs--;
}
int spam(int new_spams);
double eggs(double new_eggs);
int the_spams;
double the_eggs;
int spam(int new_spams)
{
int old_spams = the_spams;
the_spams = new_spams;
return old_spams;
}
double eggs(double new_eggs)
{
double old_eggs = the_eggs;
the_eggs = new_eggs;
return old_eggs;
}
我们想在程序 foobar 中使用它们
#include <stdio.h>
#include "foo.h"
#include "bar.h"
int main()
{
const int new_bar_spam = 3;
const double new_bar_eggs = 5.0f;
printf("foo: spam = %d, eggs = %f\n", spam(), eggs() );
printf("bar: old spam = %d, new spam = %d ; old eggs = %f, new eggs = %f\n",
spam(new_bar_spam), new_bar_spam,
eggs(new_bar_eggs), new_bar_eggs );
return 0;
}
一个问题立即变得明显:C 不知道重载。所以我们有两倍的两个函数相同的名字但不同的签名。所以我们需要一些方法来区分它们。无论如何,让我们看看编译器必须对此说:
example/ex01/ $ make
cc -c -o foobar.o foobar.c
In file included from foobar.c:4:
bar.h:1: error: conflicting types for ‘spam’
foo.h:1: note: previous declaration of ‘spam’ was here
bar.h:2: error: conflicting types for ‘eggs’
foo.h:2: note: previous declaration of ‘eggs’ was here
foobar.c: In function ‘main’:
foobar.c:11: error: too few arguments to function ‘spam’
foobar.c:11: error: too few arguments to function ‘eggs’
make: *** [foobar.o] Error 1
好吧,这并不奇怪,它只是告诉我们,我们已经知道,或者至少是怀疑的事情。
那么我们能否在不修改原始库的情况下以某种方式解决标识符冲突源代码或标题?事实上我们可以。
首先让我们解决编译时间问题。为此,我们用一个一堆预处理器 #define
指令,它们作为库导出的所有符号的前缀。稍后我们用一些漂亮舒适的包装头来做到这一点,但只是为了演示正在发生的事情是在 foobar.c 源文件中逐字执行的:
#include <stdio.h>
#define spam foo_spam
#define eggs foo_eggs
# include "foo.h"
#undef spam
#undef eggs
#define spam bar_spam
#define eggs bar_eggs
# include "bar.h"
#undef spam
#undef eggs
int main()
{
const int new_bar_spam = 3;
const double new_bar_eggs = 5.0f;
printf("foo: spam = %d, eggs = %f\n", foo_spam(), foo_eggs() );
printf("bar: old spam = %d, new spam = %d ; old eggs = %f, new eggs = %f\n",
bar_spam(new_bar_spam), new_bar_spam,
bar_eggs(new_bar_eggs), new_bar_eggs );
return 0;
}
现在如果我们编译这个...
example/ex02/ $ make
cc -c -o foobar.o foobar.c
cc foobar.o foo.o bar.o -o foobar
bar.o: In function `spam':
bar.c:(.text+0x0): multiple definition of `spam'
foo.o:foo.c:(.text+0x0): first defined here
bar.o: In function `eggs':
bar.c:(.text+0x1e): multiple definition of `eggs'
foo.o:foo.c:(.text+0x19): first defined here
foobar.o: In function `main':
foobar.c:(.text+0x1e): undefined reference to `foo_eggs'
foobar.c:(.text+0x28): undefined reference to `foo_spam'
foobar.c:(.text+0x4d): undefined reference to `bar_eggs'
foobar.c:(.text+0x5c): undefined reference to `bar_spam'
collect2: ld returned 1 exit status
make: *** [foobar] Error 1
... 乍一看,事情变得更糟了。但仔细看:其实是编译阶段一切顺利。现在只是链接器提示有符号冲突它告诉我们发生这种情况的位置(源文件和行)。正如我们所见这些符号没有前缀。
让我们用 nm 实用程序看一下符号表:
example/ex02/ $ nm foo.o
0000000000000019 T eggs
0000000000000000 T spam
0000000000000008 C the_eggs
0000000000000004 C the_spams
example/ex02/ $ nm bar.o
0000000000000019 T eggs
0000000000000000 T spam
0000000000000008 C the_eggs
0000000000000004 C the_spams
所以现在我们面临着在一些不透明的二进制文件中为这些符号添加前缀的练习。是的我知道在这个例子的过程中,我们有源并且可以在那里改变它。但现在,假设你只有那些 .o 文件,或者一个 .a(实际上只是一堆 .o)。
我们有一个特别感兴趣的工具:objcopy
objcopy 作用于临时文件,因此我们可以像就地操作一样使用它。有一个名为 --prefix-symbols 的选项/操作,您有 3 个猜测它的作用。
所以让我们把这个家伙丢给我们顽固的图书馆:
example/ex03/ $ objcopy --prefix-symbols=foo_ foo.o
example/ex03/ $ objcopy --prefix-symbols=bar_ bar.o
nm 向我们展示了这似乎有效:
example/ex03/ $ nm foo.o
0000000000000019 T foo_eggs
0000000000000000 T foo_spam
0000000000000008 C foo_the_eggs
0000000000000004 C foo_the_spams
example/ex03/ $ nm bar.o
000000000000001e T bar_eggs
0000000000000000 T bar_spam
0000000000000008 C bar_the_eggs
0000000000000004 C bar_the_spams
让我们尝试链接整个事情:
example/ex03/ $ make
cc foobar.o foo.o bar.o -o foobar
确实,它奏效了:
example/ex03/ $ ./foobar
foo: spam = 0, eggs = 0.000000
bar: old spam = 0, new spam = 3 ; old eggs = 0.000000, new eggs = 5.000000
现在我把它留给读者作为练习来实现一个自动提取使用 nm 的库的符号,写入结构的包装头文件
/* wrapper header wrapper_foo.h for foo.h */
#define spam foo_spam
#define eggs foo_eggs
/* ... */
#include <foo.h>
#undef spam
#undef eggs
/* ... */
并使用 objcopy 将符号前缀应用于静态库的目标文件。
原则上,共享库也可以这样做。然而共享库,顾名思义,在多个程序之间共享,因此以这种方式搞乱共享库并不是一个好主意。
您不会绕过编写 trampoline 包装器。更糟糕的是你不能链接到共享库在目标文件级别上,但被迫进行动态加载。但这值得单独写一篇文章。
敬请期待,祝您编码愉快。
关于c - 如何处理静态链接库之间的符号冲突?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6940384/
Java 库和 android 库有什么区别,各自有什么优点/缺点? 最佳答案 您可以在 Android 应用程序中包含标准 Java .jar 文件库。它们在 .apk 构建时被翻译成 Dalvik
所以,我现在的代码就像从 Java 层加载库(比如 liba.so),并在内部 liba.so 加载 libb.so。因此,如果我必须将所有库打包到 APK 中并将其安装在没有 root 访问权限的设
我想在我的系统中设置 LEDA 库。 我已经从以下链接下载了 LEDA 库 http://www.algorithmic-solutions.info/free/d5.php Instruct
我想用 autoconf 创建一个共享库。但是,我希望共享库具有“.so”扩展名,而不是以“lib”开头。基本上,我想制作一个加载 dlopen 的插件。 .是否有捷径可寻? 当我尝试使用 autoc
我需要在 Apps 脚本应用程序上修改 PDF。为此,我想使用 JS 库:PDF-LIB 我的代码: eval(UrlFetchApp.fetch("https://unpkg.com/pdf-lib
我正在构建一个使用以下 Boost header 的程序(我使用的是 Microsoft Visual C++ 10), #include #include #include #include
当我通过 cygwin 在 hadoop 上运行此命令时: $bin/hadoop jar hadoop-examples-*.jar grep input output 'dfs[a-z.]+' 我
我已经通过 vcpgk 成功安装了一个 C++ 库,名为:lmdb:x64-windows 我还安装了lmdb通过 Cabal 安装的 Haskell 绑定(bind)包 在尝试测试 lmdb 包时:
我该如何解决这个问题? 我刚刚将 javacv jar 文件复制到我的项目 Lib 文件夹下,但出现了这个错误! 我可以找到这个thread来自谷歌,但不幸的是,由于我国的谷歌限制政策,该页面无法打开
我有一个 Android 库项目 FooLib。 FooLib 引用 Android Context 之类的东西,但不需要任何资源文件(res/ 中的东西)所以我目前将其打包为供我的应用使用的 JAR
我正在开发一个 Android 应用程序(使用 Android Studio),它能够通过手势识别算法了解您正在进行的 Activity 。对于我使用 nickgillian ithub 帐户上可用的
关于从 .NET Framework 项目中引用 .NET Standard 类库的问题有很多类似的问题,其中 netstandard 库中的 NuGet 包依赖项不会流向 netframework
我已经从互联网上下载了 jna-4.2.2.jar,现在想将这个 jar 导入到我的项目中。但是当我试图将这个 jar 导入我的项目时,出现以下错误。 [2016-06-20 09:35:01 - F
我正在尝试通过编译在 Mac 上安装 rsync 3.2.3。但是,我想安装所有功能。为此,它需要一些库,此处 ( https://download.samba.org/pub/rsync/INSTA
进入 Web 开发有点困难。过去 5 年我一直致力于 winforms 工作。所以我正在努力从一种切换到另一种。前段时间,我使用过 JavaScript,但现在还没有大量的 JavaScript 库
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我正在寻找一个用Python编写的与logstash(ruby + java)类似的工具/库。 我的目标是: 从 syslog 中解析所有系统日志 解析应用程序特定日志(apache、django、m
就目前情况而言,这个问题不太适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、民意调查或扩展讨论。如果您觉得这个问题可以改进并可能重新开放,visit
我花了几天时间试图寻找用于 JavaPOS 实现的 .jar 库,但我找不到任何可以工作的东西。我找到了很多像这样的文档:http://jpos.1045706.n5.nabble.com/file/
这个问题在这里已经有了答案: Merge multiple .so shared libraries (2 个答案) 关闭 9 年前。 我有我在代码中使用的第三方库的源代码和对象。该库附带有关如何使
我是一名优秀的程序员,十分优秀!