9

我正在编写一个 C(共享)库。它最初是一个单一的翻译单元,我可以在其中定义几个static全局变量,对外部模块隐藏。

现在库已经增长,我想将模块分成几个较小的源文件。问题是现在我对提到的全局变量有两个选择:

  1. 在每个源文件中都有私有副本,并通过函数调用以某种方式同步它们的值——这将很快变得非常难看。

  2. 删除static定义,因此变量在所有翻译单元之间共享使用extern- 但是现在链接到库的应用程序代码可以访问这些全局变量,如果在那里进行了所需的声明。

那么,是否有一种巧妙的方法可以让私有全局变量在多个特定翻译单元之间共享?

4

4 回答 4

10

您需要 GCC 的可见性属性扩展。

实际上,类似于:

 #define MODULE_VISIBILITY  __attribute__ ((visibility ("hidden")))
 #define PUBLIC_VISIBILITY  __attribute__ ((visibility ("default")))

(您可能想要#ifdef上述宏,使用一些配置技巧 à laautoconf和其他自动工具;在其他系统上,您只会有空定义,如#define PUBLIC_VISIBILITY /*empty*/etc...)

然后,声明一个变量:

int module_var  MODULE_VISIBILITY;

或函数

void module_function (int) MODULE_VISIBILITY;

然后您可以在共享库内部使用module_var或调用,但不能在外部使用。module_function

另请参见 GCC 的-fvisibility代码生成选项。

顺便说一句,您也可以像往常一样编译-Dsomeglobal=alongname3419a6和使用整个库;someglobal要真正找到它,您的用户需要将相同的预处理器定义传递给编译器,并且您可以使名称alongname3419a6随机且不太可能,以使冲突不太可能发生。


PS。这种可见性是GCC 特有的(并且可能是 ELF 共享库,例如 Linux 上的那些)。如果没有 GCC 或共享库之外,它就无法工作......所以它是非常特定于 Linux 的(即使其他一些系统,可能是带有 GCC 的 Solaris,也有它)。可能其他一些编译器(clang来自LLVM)也可能支持Linux上的共享库(不是静态库)。实际上,真正的隐藏(对单个共享库的几个编译单元)主要由链接器完成(因为ELF共享库允许这样做)。

于 2013-03-15T14:42:22.137 回答
5

最简单的(“老派”)解决方案是根本不在预期的公共标头中声明变量。

将您的库标题拆分为“header.h”和“header-internal.h”,并在后者中声明内部内容。

当然,您还应该注意保护您的库全局变量的名称,以免它与用户代码冲突;大概您已经有一个用于此目的的函数的前缀。

您还可以将变量包装在 a 中struct,以使其更清晰,因为那时只有一个实际符号是全局可见的。

于 2013-03-15T14:51:45.177 回答
2

如果您真的想尽可能地隐藏信息,您可以使用伪装的结构来混淆事物。例如在头文件中,

struct data_s {
   void *v;
};

在您的来源中的某处:

struct data_s data;
struct gbs {
   // declare all your globals here
} gbss;

接着:

data.v = &gbss;

然后,您可以通过以下方式访问所有全局变量:((struct gbs *)data.v)->

于 2013-03-15T15:08:25.180 回答
0

我知道这不是您真正想要的,但是您可以将全局变量保留为静态并将它们分成多个源文件。

复制写入同一源文件中相应静态变量的函数,该源文件也声明为静态。

声明读取静态变量的函数,以便同一模块的外部源文件可以读取它的值。

在某种程度上使其不那么全球化。如果可能的话,将大文件分成小文件的最佳逻辑是根据数据做出决定。

如果无法做到这一点,那么您可以将所有全局变量作为静态变量放入一个源文件中,并通过函数从模块的其他源文件中访问它们,使其正式化,因此如果有人在至少你知道怎么做。但是,使用@unwind 的方法可能会更好。

于 2017-08-15T12:25:31.397 回答