在C中声明私有结构

时间:2012-10-31 20:15:49

标签: c struct private

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

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

如果无法将结构类型(如上所述 log )设为“私有”,是否意味着即使其他源文件不知道名称的存在(结构中的 log ),如果他们将一些变量命名为 log (假设我将链接所有目标文件),则仍会发生意外名称冲突?

编辑:我不熟悉编译器/链接器的工作原理。如果存在全局变量名称 log ,并且包含全局变量的文件链接到定义了结构 log 的唯一源文件,则不会导致链接时有任何混淆,一个日志是变量名,而另一个日志是类型名称?

4 个答案:

答案 0 :(得分:8)

没有。使struct私有的唯一方法是仅在使用它的文件中提供其定义 - 不要将其放在公共头文件中。如果它仅在一个源文件中使用,那么只需在该源文件中定义它,但如果它在多个源文件中使用,则会遇到一个棘手的问题:您可以在每个源文件中定义它,但是由于您有记得在进行任何更改时更改它的每个实例;或者,您可以在私有头文件中定义它,并确保只有那些源文件包含私有头。

不同源文件中的名称冲突是可以的,只要它们不尝试以任何方式相互连接即可。如果您在一个文件中定义了struct log,并且在另一个文件中定义了struct log的不同定义,则不要将一个log传递给另一个文件。在C中,结构名称不会成为目标文件中任何符号名称的一部分 - 特别是,没有名称可以包含参数类型的函数名称(如C ++那样),因为函数重载在C语言中是非法的。

答案 1 :(得分:2)

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

如果您不想在头文件中定义struct log,则不必。只需将typedef写为:

typedef struct log log;

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

关于名称冲突,请记住链接器管理的结构和类型。链接器仅关注全局可见的符号,例如函数和变量。话虽这么说,你应该为你的类型名称应用一个前缀(例如mylib_log_t)以避免混淆,特别是因为log是标准库中的数学函数。

答案 2 :(得分:0)

你有理由这样写:

static int a;

因为它阻止链接器将其与其他位置定义的a组合。
链接器struct无关,因此无需担心输入不同的c文件。
只要它在不同的c文件中,就不存在名称混淆。

答案 3 :(得分: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。

在这种情况下,应该可以做这样的事情:创建一个公共版本'头文件,并在该文件中,而不是布局完整的结构,你创建一个 undiferentiated 版本:

struct foo {
    uint8_t contents[SIZE_OF_STRUCT_FOO];
}

其中SIZE_OF_STRUCT_FOO是以常规方式定义结构时返回的sizeof(struct foo)。你基本上是在说" foo"是一个SIZE_OF_STRUCT_FOO字节的结构。然后,只要调用约定以相同的方式处理这两个结构,它就应该有效。