为啥指向未定义结构的指针有时在 C 和 C++ 中是非法的
Posted
技术标签:
【中文标题】为啥指向未定义结构的指针有时在 C 和 C++ 中是非法的【英文标题】:Why Pointers to Undefined Structs are Sometimes Illegal in C and C++为什么指向未定义结构的指针有时在 C 和 C++ 中是非法的 【发布时间】:2011-05-03 21:48:04 【问题描述】:为什么
void foo(T*);
在 C 和 C++ 中都是非法的(因为 T
未定义),而
void foo(struct T*);
是有效的,即使它仍然没有定义 T
?是否存在任何情况,无论T
是结构还是其他类型(类/枚举/typedef/等),它都会对调用者产生语义差异?
【问题讨论】:
只是出于好奇,你为什么需要一个指向未定义结构的指针?如果它是未定义的,那为什么不直接使用 void* 呢? @John:因为我还没有为T
的定义包含适当的头文件,但是我仍然希望我的代码能够编译,因为我不明白为什么@987654327 很重要@是。
@John:这是家常便饭。当您[在代码中的那个点]只需要一个声明时,为什么要引入一个[可能很重的]定义?
【参考方案1】:
void foo(struct T*);
在该范围内同时充当struct T
的前向声明,因此命名指向它的指针是安全的。
在void foo(T*)
中,不知道T
应该是什么。如果结果是变量名而不是类型名,那么这行代码格式不正确。 C++ 需要更多信息才能说这行代码是有效的。
【讨论】:
@Doug:对前向声明很感兴趣。但是,当我们只需要知道我们正在接收指针时,为什么它会变成T
呢?
@Mehrdad:因为如果T
不是一个类型(记住,“我们”不知道它是什么),那么这行代码就有问题-形成。 [我已将此添加到我的答案中。]
@Tomalak:所以它只是为了使语法更严格,而不是出于任何实现/歧义的原因?
@Mehrdad:本质上。我想说你列出的所有三个因素本质上是相互交织的。 :)
@Mehrdad:chomp 在他的回答中说得最好:“如果仅仅出现以前看不见的符号在语义上就暗示了前向类型声明,我认为我们会遇到很多麻烦。”
【参考方案2】:
在 C 中,因为指向结构(任何结构)的指针都具有相同的宽度和对齐属性
6.2.5/27 (C99 Standard)
指向 void 的指针应具有相同的表示 和对齐要求作为指向字符类型的指针。 同样,指向合格或不合格版本的指针 兼容类型应具有相同的表示和 对齐要求。所有指向结构类型的指针 应具有相同的表示和对齐方式 互相要求。所有指向联合类型的指针 应具有相同的表示和对齐方式 互相要求。指向其他类型的指针需要 没有相同的表示或对齐要求。
另一方面,指向任何东西的指针可能具有不同的宽度和/或对齐属性(void*、函数指针、unsigned char*、...)
【讨论】:
@pmg:我无法想象为什么指向联合的指针可能与指向结构的指针不同,但如果假设是这种情况,那就完全有道理了. :-) 谢谢! 这解释了为什么前向声明有效。但是,IMO,它没有回答提出的实际问题。 @Tomolak:恰恰相反。此答案是唯一回答问题的答案。 @Tomalak:问题是,我的问题不是“为什么我的代码不能编译?”但是“为什么 C/C++ 是这样设计的?”。您的回答说明了它无法编译的事实,并说明了原因,但没有说明如果取消限制会出现什么问题-另一方面,此答案解释了问题:并非全部给定平台上的指针可能相同,因此编译器只是无法生成有效代码。现在我只是想了解编译器是否可以假装它是void*
,但答案却解释了问题的根本原因。
@Tomalak:我不确定你想表达什么,但我个人认为这个答案更有说服力,因为它解释了根本原因,而不仅仅是说“就是这样” .如果别人觉得你的更有说服力,那就太好了!这就是为什么我们可以在这个网站上提供多个答案。 :]【参考方案3】:
struct T
充当 T 的forward declaration。您承诺稍后将定义它的编译器。如果你真的在 T 上做了什么或者试图实例化它,你就会遇到问题:
struct T t1; //error, incomplete type
struct T* t2; // ok
t2->foo // error, incomplete type
只是
T
是任意标识符。它是一个变量吗?它是一个函数吗?如果不在变量前添加struct
或class
,该语言无法提供转发声明功能。
【讨论】:
有趣。但是,当我们只需要知道我们正在接收一个指针时,为什么它会变成T
呢?【参考方案4】:
struct T
将T
声明为struct
,即使它出现在更大的声明中,即foo
的声明中。类型不完整,但用于声明指针函数参数时无关紧要。
没有struct
,编译器不知道T
应该是什么。
如果 T
之前已被声明为 struct
,那么 void foo(T*)
在 C++ 中将被允许,但在 C 中则不允许,因为 struct
s 的名称不会自动成为类型名称,尽管您可以声明 @987654331 @ 如果您愿意,可以在 C 中使用相同的名称。
【讨论】:
是的,但我的问题是,为什么重要?也就是说,当我们只需要知道我们正在接收指针时,T
有什么不同?
@Mehrdad:我不确定我是否理解你在说什么。这些是语言规则。任何标识符 - 对象、函数或类型 - 都需要在使用前声明,struct
使用 T
声明。
因为指向结构(任何结构)的指针都具有相同的宽度和对齐属性(6.2.5/27 "All pointers to structure types shall have the same representation and alignment requirements as each other."
)。另一方面,指向任何东西的指针可能具有不同的宽度和/或对齐属性(void*、函数指针、unsigned char*、...)
@pmg:所以一个指向结构的指针和一个指向联合/枚举/等的指针。可能有不同的对齐属性?以上是关于为啥指向未定义结构的指针有时在 C 和 C++ 中是非法的的主要内容,如果未能解决你的问题,请参考以下文章