为啥指向未定义结构的指针有时在 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

是任意标识符。它是一个变量吗?它是一个函数吗?如果不在变量前添加structclass,该语言无法提供转发声明功能。

【讨论】:

有趣。但是,当我们只需要知道我们正在接收一个指针时,为什么它会变成 T 呢?【参考方案4】:

struct TT 声明为struct,即使它出现在更大的声明中,即foo 的声明中。类型不完整,但用于声明指针函数参数时无关紧要。

没有struct,编译器不知道T应该是什么。

如果 T 之前已被声明为 struct,那么 void foo(T*) 在 C++ 中将被允许,但在 C 中则不允许,因为 structs 的名称不会自动成为类型名称,尽管您可以声明 @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++ 中是非法的的主要内容,如果未能解决你的问题,请参考以下文章

C++ 野指针规避指南!

C语言中关于结构体指针为啥不能在函数内赋初值的问题?

C ++在指向结构的指针中访问类数组指针

调用旧版 C API 时,如何在现代 C++ 中正确地将指向结构的指针转换为指向其他结构的指针?

指向 C++ 类的 C 结构指针

C++ 为啥我不能将此指针保存到引用?