C 如何合并头定义的内联函数的所有实例的静态?
有一个定义了一些C 如何合并头定义的内联函数的所有实例的静态?,c,linux,gcc,clang,elf,C,Linux,Gcc,Clang,Elf,有一个定义了一些静态内联函数的头,其中包含静态变量,如何在组成最终可加载模块的所有TU中实现相同静态局部变量的合并?。以不那么抽象的方式: /* * inc.h */ #include <stdlib.h> /* * This function must be provided via header. No extra .c source * is allowed for its definition. */ static inline void* getPtr() {
静态内联
函数的头,其中包含静态
变量,如何在组成最终可加载模块的所有TU中实现相同静态局部变量的合并?。以不那么抽象的方式:
/*
* inc.h
*/
#include <stdlib.h>
/*
* This function must be provided via header. No extra .c source
* is allowed for its definition.
*/
static inline void* getPtr() {
static void* p;
if (!p) {
p = malloc(16);
}
return p;
}
/*
* 1.c
*/
#include "inc.h"
void* foo1() {
return getPtr();
}
void* bar1() {
return getPtr();
}
/*
* 2.c
*/
#include "inc.h"
void* foo2() {
return getPtr();
}
void* bar2() {
return getPtr();
}
很可能两个TU都会收到自己的getPtr.p
。尽管在每个TU中,getPtr.p
在所有getPtr()实例化中共享。这可以通过检查最终可加载二进制文件来确认:
$ readelf -s --wide a.out | grep getPtr
32: 0000000000201030 8 OBJECT LOCAL DEFAULT 21 getPtr.p
34: 0000000000201038 8 OBJECT LOCAL DEFAULT 21 getPtr.p
同时,我正在寻找一种方法,来跨单独的TU边界共享getPtr.p
。这与C++模板实例化的情况类似。很可能GRP\u COMDAT
会对我有所帮助,但我找不到任何关于如何标记要放入COMDAT的静态var的信息
是否有任何属性或其他源代码级别(不是编译器选项)的方法来实现合并此类对象?如果我正确理解您的要求,只需声明一个全局变量即可实现此效果
/*
*公司
*/
无效*我的;
静态内联void*getPtr(){
如果(!我的){
my_p=malloc(16);
}
归还我的护照;
}
这将在整个程序中对getPtr
的所有实例使用相同的变量my_p
(因为它是全局变量)。而且在任何模块中都不需要明确定义my\p
。它将被初始化为NULL
,这正是您想要的。因此,除了inc.h
之外,不需要更改任何内容,也不需要额外的.c
文件
当然,您可能希望给my_p
一个不太可能与用户程序中的任何标识符冲突的名称。可能是Sergios\u包含\u getPtr的\u文件\u p\u或类似的东西
这实际上是标准C的扩展(在N2176的附录J.5.11中提到),但它是由gcc和clang在大多数现代平台上提供的。它记录在-fcomon
编译器选项下(默认情况下启用)。它通常通过将变量放在common
部分来实现,然后链接器将所有实例合并在一起,就像您建议的那样。但是上面的代码显示了如何访问该功能,而不需要使用属性或其他模糊的咒语
如果你想变得特别偏执,你可以用\uu属性((公共))
声明my\u p
,这将导致变量以这种方式处理,即使-fno-common
有效。(当然,如果由于某种原因而使用了-fno common
),则可能会导致问题…所有TU中相同的静态局部变量
-So。。难道你不能通过提供一个变量的单一定义,将定义移动到文件范围并合并所有TU中的变量吗?为什么你要求在每个文件中都有一个getPtr代码和静态变量的实例,而在每个文件中,你#包括头后,要求所有文件都只有一个实例?因为你想让函数内联?@KamilCuk是的,这显然是正确的方法。我只是忘了强调我的函数必须在标题中。你可以把它看作是一个“只有标题的图书馆”。布鲁诺因为我不够清楚而责骂我。正如我在上面的评论中所说,定义必须放在标题中。您不能共享静态变量,但可以共享分配的内存,而不是在每个内存中调用malloc,而是使用任何方式来获得相同的分配缓冲区。静态变量不使用大量内存,这与缓冲区不同,缓冲区可能很大!我完全忘记了这些“未分配的外部程序”。是的,这对我来说很有效,不过正如你所说的,数据隔离有所损失。编译器似乎甚至没有把它放到某个部分,而是将这个对象标记为SHN_COMMON
。现在链接器负责为它分配一个槽位。我刚刚发现,不幸的是,若在cpp源代码中包含头文件,那个么它就不会进行得很顺利。它在每个TU中分配对象,因此链接步骤失败。@塞尔吉奥:也许你想问一个单独的问题,关于如何在C++中这样做。我对那种语言知之甚少。
$ readelf -s --wide a.out | grep getPtr
32: 0000000000201030 8 OBJECT LOCAL DEFAULT 21 getPtr.p
34: 0000000000201038 8 OBJECT LOCAL DEFAULT 21 getPtr.p