假设我们有两个结构:
typedef struct Struct1
{
short a_short;
int id;
} Struct1;
typedef struct Struct2
{
short a_short;
int id;
short another_short;
} Struct2;
从Struct2 *
投射到Struct1 *
是否安全? ANSI规范对此有何规定?
我知道有些编译器可以选择重新排序结构字段以优化内存使用,这可能会使两个结构不兼容。有没有办法确定这段代码是否有效,无论编译器标志如何?
谢谢!
答案 0 :(得分:5)
据我所知,这是安全的。
但如果可能的话,做得好得多:
typedef struct {
Struct1 struct1;
short another_short;
} Struct2;
然后你甚至告诉编译器Struct2
以Struct1
的实例开头,并且因为指向结构的指针总是指向它的第一个成员,所以你可以安全地对待{ {1}} Struct2 *
。
答案 1 :(得分:3)
struct指针类型在C中始终具有相同的表示。
(C99,6.2.5p27)“所有指向结构类型的指针都应该相同 代表和对齐要求彼此。“
结构类型中的成员总是在C中按顺序排列。
(C99,6.7.2.1p5)“结构是由序列组成的类型 成员,其存储按有序顺序分配“
答案 2 :(得分:3)
它很可能会奏效。但是你在询问如何确保这些代码有效时你是非常正确的。所以:你的程序中的某个地方(在启动时可能)嵌入了一堆ASSERT语句,确保offsetof( Struct1.a_short )
等于offsetof( Struct2.a_short )
等。此外,除了你以外的一些程序员可能有一天会修改其中一个结构但不是另一个,所以比抱歉更安全。
答案 3 :(得分:2)
语言规范包含以下保证
6.5.2.3结构和工会成员
6 为了简化工会的使用,我们提供了一项特殊保证:如果工会包含 几个结构共享一个共同的初始序列(见下文),如果是联盟 对象当前包含这些结构中的一个,允许检查公共结构 其中任何一个的初始部分都是完整类型的联盟的声明 是可见的。如果相应的成员,两个结构共享共同的初始序列 对于一个或多个序列具有兼容类型(并且对于位字段,具有相同的宽度) 初始成员。
这仅适用于通过工会打字。但是,这基本上保证了这些结构类型的初始部分将具有相同的内存布局,包括填充。
以上并不一定允许通过生成不相关的指针类型来做同样的事情。这样做可能会违反别名规则
6.5表达式
7 对象的存储值只能由具有其中一个的左值表达式访问 以下类型:
- 与对象的有效类型兼容的类型,
- 与对象的有效类型兼容的类型的限定版本,
- 对应于有效类型的有符号或无符号类型的类型 对象,
- 对应于合格版本的有符号或无符号类型的类型 有效的对象类型,
- 聚合或联合类型,其中包括上述类型之一 成员(包括递归地,子集合或包含的联合的成员)或
- 字符类型。
这里唯一的问题是访问
((Struct1 *) struct2_ptr)->a_short
构成对整个Struct2
对象的访问(在这种情况下,它违反了6.5 / 7且未定义),或仅仅访问short
对象(在这种情况下可能是完美定义)。
一般情况下,坚持以下规则可能是一个好主意:通过工会允许类型惩罚,但不允许通过指针。不要通过指针来做,即使你正在处理两个具有成员共同初始子序列的struct
类型。
答案 4 :(得分:1)
不,标准不允许这样做;通过Struct1指针访问Struct2对象的元素是未定义的行为。 Struct1和Struct2不是兼容类型(如6.2.7中所定义),可以填充不同,并且通过错误的指针访问它们也会违反别名规则。
这样的事情唯一可行的方法是当Struct1作为其初始成员(标准中的6.7.2.1.15)包含在Struct2中时,就像在unwind's answer中一样。
答案 5 :(得分:-1)
是的,可以这样做!
示例程序如下。
#include <stdio.h>
typedef struct Struct1
{
short a_short;
int id;
} Struct1;
typedef struct Struct2
{
short a_short;
int id;
short another_short;
} Struct2;
int main(void)
{
Struct2 s2 = {1, 2, 3};
Struct1 *ptr = &s2;
void *vp = &s2;
Struct1 *s1ptr = (Struct1 *)vp;
printf("%d, %d \n", ptr->a_short, ptr->id);
printf("%d, %d \n", s1ptr->a_short, s1ptr->id);
return 0;
}