27

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

struct foo;

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

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

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

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

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

&运算符是:

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

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

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

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

这是 gcc 的缺陷吗?

4

3 回答 3

7

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

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

基本上,只要您不使用需要了解struct. 因此,如果您不访问该对象或询问其大小,这是合法的。

于 2011-08-04T08:32:51.197 回答
3

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

§6.5.3.2 地址和间接运算符

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

这意味着&*j等价于j

但是,j应该是指向对象的指针,并且它只是指向不完整类型的指针,正如 GCC 4.4.5 所说。

§6.3.2.3 指针

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

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

因此,问题中的这种观察是不正确的:

两者在这里都得到了微不足道的满足,

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


§6.2.5 类型

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

于 2011-08-04T09:24:30.047 回答
2

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

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

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

于 2011-08-04T07:56:32.247 回答