2012-04-25 39 views
0

C中反对声明一个结构成员的规则似乎是我必须包含守卫的主要原因。如果我们有在“header.h”以下内容:为什么不允许多次定义一个C结构的成员?

struct s { 
    int a; 
    char b; 
}; 

和文件“啊”#包括的header.h,那么我们不能包括“啊”和“header.h”是那么的struct是定义两次。

我的问题是,这样做的问题是什么?为什么不能允许多个相同的结构定义被允许?这将消除对包含警卫的需求,并且极大地清理C头文件。

C中的规则是允许多个声明,但只有一个定义。出于某种原因,指定结构体的成员称为“定义”,尽管它没有定义变量或函数。

+0

然而,你*是*,当你指定成员时定义'struct'的结构。 – 2012-04-25 17:16:23

+0

删除包含警卫不会“清理”任何东西。它只会使编译速度变慢,因为编译器会重复读取并重新解析相同的头文件。 – 2012-04-25 17:52:14

回答

1

重新定义结构是非常容易出错的。即使在你的简单例子中,如果a.h包含一个#pragma,它在包含“header.h”之前调整结构打包或对齐,那么这两个定义可能不再一样。这种类型的问题很难调试,因为它依赖于包含头部的顺序。

一般来说,当您允许数据​​类型重新定义时,有许多事情可能会出错。作为交换,除了能够丢弃头部卫士之外,你从它身上得不到任何真正的好处。标题警卫解决问题,并且只是一小部分开销,不会不合理地混淆代码。一些编译器支持作为包含守护进程的#pragma once(或类似的),但只需要一行代码。

我个人认为,这将是一个更好的主意,在默认情况下,以防止多包容,只需要对那些设计被列入多次(离开开销的使用情况是在头后卫宏广大少数民族)。但这并不是C最初实现的方式(加上它会使预处理器更复杂),所以它不可能改变。

+0

结构包装选项是我没有考虑过的东西。如果头文件中的预处理器声明(例如“#pragma pack”)在头文件外生效,可能很难找出两个看起来相同的定义不同的原因,编译器可能甚至不会告诉你它们是不同的。对此的明显解决方案是使这样的声明仅持续用于一个源文件,但是这不会是向后兼容的。 – 2012-04-25 18:30:17

+0

我认为“向后兼容”是这里的关键问题。 C从一开始就表现出这样的态度,这是我们学会与之共处的东西。改变它会很好,但它会打破现有的代码,它永远不会发生。如果你看看比C小的语言,你会发现他们中的很多人都实现了他们的'#include'模拟器来解决C语言中的烦恼(请参阅Objective C中的#import或Ruby中的require)。 – bta 2012-04-25 21:35:26

+0

我会说“这不是一个错误 - 这是一个功能”。如果改变'#include'指令的顺序会改变一个'struct'定义的语义,最好是多次定义一个结构,重复的定义会导致一个错误*当且仅当结果结构与原始*不匹配,因为包含后卫会跳过第二个定义。 – supercat 2012-06-13 23:53:39

0

嗯,这就像C工程...这是定义,因为它提供了对象的实际实现。

0

你不能两次定义它们,因为@Kirilenko说,定义提供了对象的实现。不过,我想知道你的问题是否也在问别的问题。

编辑:将实例化改为定义。

你的S的定义

struct s { 
    int a; 
    char b; 
}; 

将进入一个.c文件,或者至少,你要么在的extern结构在.h文件,并把结构实例的.c文件,或使用在.h文件中输入typedef。

typedef struct s { 
    int a; 
    char b; 
} my_struct_type; 

即使你想要在a中定义的结构。h文件,你可以防止其“被定义两次”被包围,像这样

编辑:从宏观 删除了下划线的#ifdef MY_STRUCT_DEFINED 的struct { INT一个; char b; }; #endif

+0

丢失宏名称上的前导下划线。他们保留。 – 2012-04-25 17:53:31

+0

谢谢。他们保留在C?我会记下它,并编辑答案以删除警卫。 – octopusgrabbus 2012-04-25 17:55:00

+0

是的。非正式地说,它们是为系统头文件而保留的,因此它们不会与应用程序宏发生冲突,并且您的应用程序宏不会与系统头文件冲突。 – 2012-04-25 17:55:52

相关问题