(我说的是C,但它也适用于C ++中的类模板)
在头文件中,放置所有DECLARATIONS而不是定义是自定义的。但是,我们通常将结构定义或类模板放在头文件中,而实际上并不知道为什么我们可以。这实际上没有意义,因为它们也是定义 - 一个定义规则。 (是的,结构定义和类模板不会导致设置任何存储,但是仍然会出现“重新定义”错误,这意味着它们是定义)。
EX)在同一个文件中定义多个具有相同标记的结构会给出重新定义错误,但在多个源文件中定义多个具有相同标记的结构不会导致任何错误(同样的事情发生在类中)。
唯一有意义的是结构定义和类模板具有内部链接(与默认外部链接相对),但我在K& R或参考手册中找不到任何关于它的参考。 事实上,甚至没有提到结构链接。
我想知道ANSI标准指出这种现象的确切参考。 (国际海事组织,这是一个非常模糊的事情,在某些地方有ANSI标准提及。)
修改 我不是在问为什么可以将结构定义放入头文件中。
我在问为什么在头文件中放置结构定义不会导致重定义错误,就像我们将变量定义放在头文件中一样(并将其包含在多个源文件中)
EX) test1.c:int a = 3; test2.c:int a = 4; 由于重新定义导致编译错误。然而,
test1.c:struct test {int a}; test2.c:struct test {int b}; 不会导致编译错误,我能想出的唯一原因是 结构定义要么具有内部联系,要么根本没有联系。
答案 0 :(得分:4)
在C中,只有对象和函数具有链接。由于C中的struct
可能不包含函数或“static
”成员对象,因此在C ++中你的问题没有多大意义,这里。
C ++中的成员函数,只要它们没有定义,但只在struct
中声明没有问题。如果它们也被定义,它们是inline
。 inline
的概念刚刚为C ++发明,用于捕获这种情况:一个函数 definition ,它可以通过几个编译单元中的头文件共享。采用该概念的C99(略微修改)。
static
成员对象确实造成了更大的问题。关于如何实例化这些人的语法非常模糊,特别是对于template
class
es或struct
s。如果你想知道那个,你必须要求它,专门用C ++标记。
答案 1 :(得分:1)
结构线只是一个定义。该定义在源文件之外是不可见的 仅供参考:两个源文件都没有输出任何内容。
测试一下:
$ cat test1.c
struct test { char a; };
$ gcc -o test1.o -c test1.c
$ nm
$ echo "struct test foo; " >> test1.c
$ gcc -o test1.o -c test1.c
$ nm
0000000000000001 C _foo
答案 2 :(得分:0)
结构在头文件中定义,因为头文件提供了模块的接口。当在接口中定义结构时,接口的用户可以:
Linkage与它无关 - 只有函数是链接的,而不是数据结构。
请注意,您仍然可以在头文件中声明结构,如果要隐藏结构的内部(不透明数据结构),这很有用。接口的用户可以指向结构并将它们用作cookie的类型,但是他们不能自己分配这样的结构,或者在其中“看到”。
至于在头文件中定义结构时没有得到重定义错误,这只是因为标题保护 - 标题通常如下所示:
#ifdef MYHEADER_H
#define MYHEADER_H
struct a { int x; }
void f(void);
/* and so on */
#endif
因此,当包含头文件时,它通常只包含一次,因此每个翻译文件只定义一次结构。链接器与结构定义无关,因为它们没有链接。
答案 3 :(得分:0)
我认为你在这里混淆了一下,你可以把任何你喜欢的东西放在头文件中。
通常的内容是枚举,typedef,结构和函数原型的声明,因此各种C文件可以编译而无需了解实际函数或实际内存(结构基本上是内存的定义)布局)
答案 4 :(得分:0)
我没有最终版本的副本,但是从n843 draft of the specification我看到了:
来自 6.7.2.3标记:“4结构,联合或枚举类型的两个声明位于不同的范围或使用不同的标记声明不同的类型。结构,联合或的每个声明不包含标记的枚举类型声明了一个不同的类型。“
来自 6.2.1标识符范围:“4每个其他标识符的范围由其声明的位置(在声明者或类型指定者中)确定。如果声明者或类型指定者声明了标识符出现在任何块或参数列表之外,标识符具有文件范围,它终止于翻译单元的末尾。[...]“[原文强调](唯一标识符类型)在本声明之前我在本节中提到的是标签,它具有功能范围。)
我不是C或标准方面的专家,但我认为这样可以解释您所看到的行为。因此,如果文件范围相同且结构具有重复标记,则结构应该具有冲突,因为需要具有不同标记以使它们成为不同类型。但如果它们处于不同的文件范围内,则没有问题,因为只要它们处于不同的范围内,就可以满足不同类型的要求。