在C中模仿静态对象的构造函数
Posted
技术标签:
【中文标题】在C中模仿静态对象的构造函数【英文标题】:Imitating constructor of static objects in C 【发布时间】:2011-10-21 11:33:13 【问题描述】:我想要一个全局的names
变量,看起来像这样
char* names[NAMES_CAP];
int names_len = 0;
我希望每个链接到这个库的人都能够向这个列表添加一个项目。
main
很容易做到这一点。
int main(int argc,char**argv)
names[names_len++] = "new name";
names[names_len++] = "new name 2";
但是如果我想堆叠两个库怎么办? (即,我的库,libnames
保存全局变量。如果有人链接到使用libnames
的libnameuser
,它会自动将libnameuser
中定义的所有名称添加到libnames
中的names
数组中。
有什么办法吗?
在 C++ 中,我可以将 names[names_len++] = "..."
插入到全局对象的构造函数中,并且必须调用它。但是我可以用普通的 C 来做到这一点吗?
【问题讨论】:
【参考方案1】:如果你使用 gcc,你可以使用构造函数属性__attribute__((constructor))
来获得相同的效果。然而,这是非标准的 C。
但是我建议不要使用这种模式,因为在 main 之前运行的任何函数的顺序都无法控制。在 main 开始运行后,我宁愿找到一种很好的方法来挂钩所有“构造函数”。
【讨论】:
我不介意顺序。我会很高兴听到实现类似目标的更好模式。我想尽可能地让用户感到轻松。【参考方案2】:更新:有关此答案的更新版本,请参阅 https://***.com/a/2390626/270788。
下面是一个预处理器抽象,用于支持 GCC 和 MSVC 的 C 静态初始化函数。 GCC 版本也适用于 LLVM CC,也许其他一些编译器也是如此。
MSVC 版本的工作原理是将静态初始化函数的 ptr 放置在由应用程序或 DLL 启动代码处理的特殊部分中。
#if defined(__GNUC__)
#define INITIALIZER(f) \
static void f(void) __attribute__((constructor)); \
static void f(void)
#elif defined(_MSC_VER)
#define INITIALIZER(f) \
static void __cdecl f(void); \
__declspec(allocate(".CRT$XCU")) void (__cdecl*f##_)(void) = f; \
static void __cdecl f(void)
#endif
INITIALIZER(initializer_1) names[names_len++] = "new name";
INITIALIZER(initializer_2) names[names_len++] = "new name 2";
【讨论】:
请参阅***.com/a/2390626/270788 了解此答案的更新版本。【参考方案3】:如果您不是 100% 关心可移植性并且可以保证 GCC,那么您可以使用 GCC 中的“构造函数”属性来做您想做的事情。见:http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Function-Attributes.html。
不过,我同意@Anders K 的观点,他说您应该将这种行为封装在函数中。
【讨论】:
【参考方案4】:我认为为了实现您想要的效果,最好为您的 names[] 数组创建几个包装函数。然后访问您的“全局”名称数组的模块必须通过该接口。
这将允许您更好地控制和解耦
然后您可以将数组放在一个模块中,然后在标题中公开这些函数,但保持数组隐藏。
【讨论】:
但问题仍然存在:如何从库代码中自动执行这些函数? @MagnusHoff 说了什么。我的问题不是阻止访问names
,而是强制用户在访问我的库之前更新names
。【参考方案5】:
我认为全局变量的初始化也适用于 C。
int func1();
int someGlobalVariable = func1();
int func1()
/* Your initialization code can go here */
我希望我没有误会这在 C 中工作(很长时间以来一直只使用 C++)。
【讨论】:
以上是关于在C中模仿静态对象的构造函数的主要内容,如果未能解决你的问题,请参考以下文章