【问题标题】:Is &*p valid C, given that p is a pointer to an incomplete type?鉴于 p 是指向不完整类型的指针,&*p 是有效的 C 吗?
【发布时间】:2011-10-19 17:19:15
【问题描述】:

以下示例是 C 中有效的完整翻译单元吗?

struct foo;

struct foo *bar(struct foo *j)
{
    return &*j;
}

struct foo 是一个不完整类型,但我在 C 标准中找不到明确禁止取消引用不完整类型。特别是,§6.5.3.2 说:

一元& 运算符产生其操作数的地址。如果 操作数的类型为“type”,结果的类型为“pointer to type”。如果 操作数是一元 * 运算符的结果,也不是 运算符也不是 & 运算符被评估,结果就像 两者都被省略了,除了对运算符的约束仍然 应用,结果不是左值。

结果不是左值这一事实并不密切 - 返回值不必如此。 * 运算符的约束很简单:

一元*运算符的操作数应为指针类型。

& 运算符上是:

一元& 运算符的操作数应为函数 指示符,[] 或一元 * 运算符或左值的结果 指定一个不是位域且未声明的对象 使用 register 存储类说明符。

这里两者都满足,所以结果应该等同于return j;

但是,gcc 4.4.5 不编译此代码。相反,它给出了以下错误:

y.c:5: error: dereferencing pointer to incomplete type

这是 gcc 的缺陷吗?

【问题讨论】:

  • 我认为你是对的。这肯定不会是第一个,也不会是最后一个……
  • 从技术上讲,只有 C99 有这个措辞。 C90 没有提到&* 相互抵消。对于C99,我同意你的分析。但由于 gcc 并未声称完全符合 C99,因此我对这个错误消息并不感到惊讶。

标签: c pointers language-lawyer incomplete-type


【解决方案1】:

是的,我认为这是一个错误。根据上下文,即使是不完整类型的左值,例如*j,似乎也是允许的:

6.3.2.1 ... 左值是具有对象类型或 void以外的不完整类型

基本上,只要您不使用需要了解struct 结构的左值做任何事情,这应该可以工作。因此,如果您不访问该对象或询问其大小,这是合法的。

【讨论】:

  • 是的,这将是下一个合乎逻辑的问题......例如,根据我的阅读,也应该允许使用像 (void)*j 这样的空表达式。
【解决方案2】:

C99 标准 (ISO/IEC 9899:1999) 描述了这种行为:

§6.5.3.2 地址和间接运算符

一元 & 运算符返回其操作数的地址。如果操作数的类型为“type”, 结果的类型为“指向类型的指针”。如果操作数是一元 * 运算符的结果, 该运算符和 & 运算符都没有被计算,结果就像两者都是 省略,除了对运算符的约束仍然适用并且结果不是 左值。

这意味着&*j 等价于j

然而,j 应该是一个指向对象的指针,它只是一个指向不完整类型的指针,正如 GCC 4.4.5 所说的那样。

§6.3.2.3 指针

指向 void 的指针可以转换为指向任何不完整或对象的指针或从指针转换为指向任何不完整或对象的指针 类型。指向任何不完整或对象类型的指针都可以转换为指向 void 的指针 又回来了;结果应与原始指针比较。

注意它区分了对象类型和不完整类型;这在标准中经常出现。

所以,问题中的这个观察是不正确的:

这里都满足,

变量j不是指向对象的指针;它是一个指向不完整类型的指针,它不是一个对象。


§6.2.5 类型

[...] 类型 分为对象类型(完全描述对象的类型)、函数类型(类型 描述函数)和不完整类型(描述对象但缺少 确定其大小所需的信息)。

【讨论】:

  • 您的分析中似乎缺少的部分是 where 特别需要指向 object 的指针。对 * 运算符的操作数的唯一约束似乎是它具有“指针类型”,它既包含指向对象的指针,也包含不完整的类型。
  • 我认为j 仍然是一个指向对象的指针。只是它的类型不完整。例如,该标准明确讨论了不完整类型的左值。指向对象的事实仅取决于正确分配的事实(大小和对齐方式)。
  • @Jonathan,它不是和我说的完全一样吗? “不完整类型”是“描述对象但缺乏信息的类型......”。因此,特别是指向不完整类型的指针是指向对象的指针。并且取消引用这样的指针会产生一个左值。这种不完整类型的左值在别处明确提到。
  • @jens:不。它说有完整的对象类型;有不完整的类型(根据“分区”的定义,与对象类型不同),还有函数类型。所以对象类型与不完整类型不同,也不相同。
  • @Jonathan 不,它没有这么说。您引用的部分是“描述对象的类型”,没有任何含糊之处。
【解决方案3】:

是的。 C 中的指针通常具有相同的大小(在某些嵌入式系统上,它们可能不同)。这意味着即使类型是“未知”,编译器也可以为此生成正确的汇编代码。

您可以使用这种方法将内部数据结构完全隐藏到外部。使用typedef 声明指向结构的指针,并且仅在内部头文件(即不属于公共 API 的文件)中声明该结构。

gcc 4.4.5 抱怨的原因仅仅是:如果你在实现之外使用指向不完整类型的指针,它应该可以工作。但是代码是实现的一部分,在这里,您可能希望拥有完整的类型。

【讨论】:

  • C 中的所有指针都具有相同的大小是不对的。发生这种情况的平台很少,但是例如在具有哈佛架构的嵌入式系统中,您可以找到数据和函数指针的不同指针大小。
  • @flolo:正确。这甚至在最近的一个问题中讨论过。有人发布了几个平台的链接,其中不同类型的指针具有不同的大小。
  • C99 标准的相关部分是第 6.2.5 节,第 27 节:“所有指向结构类型的指针应具有彼此相同的表示和对齐要求。”;这不适用于任意指针类型
猜你喜欢
  • 2014-12-22
  • 1970-01-01
  • 2020-04-30
  • 1970-01-01
  • 2015-10-09
  • 2012-07-26
  • 2019-07-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多