在C语言中声明私有结构体

3

是否可以声明一个仅在使用该结构的.c文件中可见的结构类型?我知道通过在外部数据对象前面放置static,可以将变量的链接性更改为内部。但是是否可以在新的struct类型声明前面放置static,如下所示:

static struct log{
            ...;
            ...;
};
typedef struct log log;

如果结构类型(例如在上面的代码中)无法设置为“私有”,是否意味着即使其他源代码不知道该结构的名称(在本例中为“log”),如果它们将某些变量命名为“log”(假设我将链接所有目标文件),仍然可能发生意外的名称冲突?
编辑:我不熟悉编译器/链接器的工作原理。如果存在全局变量名为“log”,并且包含全局变量的文件与定义结构“log”的唯一源文件链接,那么在链接时会不会造成混淆,因为一个“log”是变量名称,而另一个“log”是类型名称?
4个回答

9
不行。使一个结构体私有的唯一方法是仅在使用它的文件中提供其定义 - 不要将其放在常见头文件中。如果它只在一个源文件中使用,则只需在该源文件中定义它;但是,如果它在多个源文件中使用,则会出现棘手的问题:你可以在每个源文件中定义它,但这样很脆弱,因为你必须记住在进行任何更改时更改每个实例;或者,可以在一个私有头文件中定义它,并确保只有那些源文件包含私有头文件。
不同源文件中的名称冲突是可以的,只要它们不试图以任何方式进行接口。如果你在一个文件中定义了一个名为“struct log”的结构体,并在另一个文件中定义了不同的“struct log”定义,请勿将一个“log”传递给另一个。在C中,结构体名称不会成为对象文件中任何符号名称的一部分 - 特别地,没有函数名称的名称重整来包括参数类型(像C++那样),因为在C中,函数重载是非法的。

那么在另一个源文件中,我可以定义一个全局变量“int log;”,甚至定义另一个也名为“log”的结构体吗?如果“log”已知为类型名称,我认为你不能这样做“double log;”。 - Rich

3

不,static 是一种存储类型;将其应用于变量声明之外的类型是没有意义的。

如果你不想在头文件中定义 struct log,你也可以不定义。只需将 typedef 写为:

typedef struct log log;

只要你只处理log *指针,那么这就足够了。但是,你需要完整定义结构来声明一个log(或者使用sizeof(log)),因为结构的大小取决于它包含的内容。

关于名称冲突,请记住结构和类型不会由链接器管理。链接器只关心全局可见的符号,例如函数和变量。话虽如此,你应该给你的类型名加上前缀(例如mylib_log_t),以避免混淆,特别是因为log是标准库中的数学函数。


0
你有写这个的理由:
static int a;

因为它可以防止链接器将其与其他地方定义的a组合在一起。
链接器struct无关,因此可以放心地放在不同的c文件中。
只要它们在不同的c文件中,就不会出现名称混淆。


0

一般情况下这是不可能的。但我可以想到一个可能在某些编译器上起作用的方法。

之所以难以实现是因为C编译器需要知道结构体的样子,才能生成调用函数并将结构体实例作为参数传递的代码。

因此,假设你定义了一个带有以下头文件的库:

struct foo {
    int32_t a, b;
};

foo make_foo(int arg);

foo do_something(foo p1, foo p2);

然后编译调用do_something的程序时,编译器通常需要知道结构体foo的样子,以便将其作为参数传递。编译器在这里可以做各种奇怪的事情,例如通过寄存器传递结构体的一部分,通过堆栈传递结构体的另一部分,因此它确实需要知道结构体的样子。

但是,我相信在某些编译器中,可以指示整个结构体应通过堆栈传递。例如,如果您的目标架构是i386(docs),则regparm(0)函数属性应该适用于GCC。

在这种情况下,可以像这样做:创建一个“公共版本”的头文件,在该文件中,不要布置完整的结构体,而是创建一个未区分的版本:

struct foo {
    uint8_t contents[SIZE_OF_STRUCT_FOO];
}

这里的SIZE_OF_STRUCT_FOO是指在通常情况下定义结构体时,sizeof(struct foo)返回的大小。你基本上是在说“foo”是一个有SIZE_OF_STRUCT_FOO字节的结构体。只要调用约定以相同方式处理这两个结构体,它就应该能正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接