以下示例是C中的有效完整翻译单元吗?
struct foo;
struct foo *bar(struct foo *j)
{
return &*j;
}
struct foo
是一个不完整的类型,但我无法明确禁止在C标准中解除引用不完整类型。特别是,§6.5.3.2说:
一元
&
运算符生成其操作数的地址。如果 操作数具有类型''type'',结果具有类型''指向类型''的指针。如果 操作数是一元*
运算符的结果,也不是 运算符和&
运算符都被计算,结果就像是 除了对运算符的约束之外,两者都被省略了 申请,结果不是左值。
结果不是左值的事实不是密切相关的 - 返回值不一定是。 *
运算符的约束只是:
一元*运算符的操作数应具有指针类型。
和&
运算符是:
一元
&
运算符的操作数应该是一个函数 指示符,[]
或一元*
运算符的结果,或左值 指定不是位字段且未声明的对象 使用register
存储类说明符。
这两个都非常满意,因此结果应该只相当于return j;
。
但是,gcc 4.4.5不编译此代码。它反而出现以下错误:
y.c:5: error: dereferencing pointer to incomplete type
这是gcc中的缺陷吗?
答案 0 :(得分:6)
是的,我认为这是一个错误。即使是不完整类型的左值,所以*j
似乎也允许,具体取决于上下文:
6.3.2.1 ... 左值是一个带有对象类型或表达式的表达式 除void之外的不完整类型
基本上,只要你不对需要了解struct
结构的左值做任何事情,这应该有效。因此,如果您不访问该对象或询问其大小,这是合法的。
答案 1 :(得分:3)
C99标准(ISO / IEC 9899:1999)描述了行为:
§6.5.3.2地址和间接运算符
一元&运算符返回其操作数的地址。如果操作数的类型为''type'', 结果有''指向类型''的类型。如果操作数是一元*运算符的结果, 无论是运营商还是&运算符被评估,结果就像两者都是 省略,除了对运算符的约束仍然适用且结果不是a 左值。
这意味着&*j
相当于j
。
但是,j
应该是指向对象的指针,它只是指向不完整类型的指针,正如GCC 4.4.5所说。
§6.3.2.3指针
指向void的指针可以转换为指向任何不完整或对象的指针 类型。指向任何不完整或对象类型的指针可以转换为指向void的指针 又回来了;结果应该等于原始指针。
请注意,它区分对象类型和不完整类型;这在标准中经常发生。
所以,问题中的这个观察是不正确的:
这两方面都非常满意,
变量j
不是指向对象的指针;它是一个指向不完整类型的指针,它不是一个对象。
§6.2.5类型
[...]类型 被分为对象类型(完全描述对象的类型),函数类型(类型 描述函数)和不完整类型(描述对象但缺乏的类型) 确定其大小所需的信息。)
答案 2 :(得分:2)
是。 C中的指针通常具有相同的大小(在某些嵌入式系统中,它们可以不同)。这意味着编译器可以为此生成正确的汇编程序代码,即使类型是“未知”。
您可以使用此方法将内部数据结构完全隐藏到外部。使用typedef
声明指向结构的指针,并仅在内部头文件中声明结构(即不属于公共API的文件)。
gcc 4.4.5抱怨的原因就在于:如果你在实现之外使用指向不完整类型的指针,它应该可行。但代码是实现的一部分,在这里,您可能希望拥有完整的类型。