C语言 结构指针兼容性

i2byvkas  于 2023-03-28  发布在  其他
关注(0)|答案(6)|浏览(170)

假设我们有两个struct:

typedef struct Struct1
{
    short a_short;
    int id;
} Struct1;

typedef struct Struct2
{
    short a_short;
    int id;
    short another_short;
} Struct2;

Struct2 *强制转换到Struct1 *安全吗?ANSI规范对此有什么规定?我知道一些编译器可以选择重新排序结构体字段以优化内存使用,这可能会导致两个结构体不兼容。有什么方法可以确保这段代码有效,而不管编译器标志是什么?
谢谢大家!

r55awzrz

r55awzrz1#

它是安全的,据我所知。
但如果可能的话,最好是:

typedef struct {
    Struct1 struct1;
    short another_short;
} Struct2;

然后你甚至告诉编译器,Struct2Struct1的一个示例开始,由于指向结构体的指针总是指向它的第一个成员,所以你可以安全地将Struct2 *视为Struct1 *

uttx8gqw

uttx8gqw2#

结构指针类型在C中总是具有相同的表示。
(C99,6.2.5p27)“指向结构类型的所有指针应具有彼此相同的表示和对齐要求。”
而结构类型中的成员在C中总是按顺序排列的。
(C99,6.7.2.1p5)“结构是由一系列成员组成的类型,其存储按有序序列分配”

svmlkihl

svmlkihl3#

不,标准不允许这样做;通过Struct 1指针访问Struct 2对象的元素是未定义的行为。Struct 1和Struct 2是不兼容的类型(如6.2.7中定义的),可以以不同的方式填充,并且通过错误的指针访问它们也违反了别名规则。
只有当Struct 1作为其初始成员包含在Struct 2中时(标准中为6.7.2.1.15),才能保证这样的工作,如unwind's answer

but5z9lq

but5z9lq4#

语言规范包含以下保证

6.5.2.3结构和工会成员
6为了简化工会的使用,我们提供了一项特殊保证:如果一个并集包含几个共享一个公共初始序列的结构(见下文),并且如果联合对象当前包含这些结构之一,则允许在联合的完整类型的声明可见的任何地方检查它们中任何一个的公共初始部分。如果对应的成员具有兼容的类型,则两个结构共享公共初始序列(并且,对于位字段,相同的宽度)。

这只适用于通过联合进行的类型双关。然而,这基本上保证了这些结构类型的初始部分将具有相同的内存布局,包括填充。
上面并不一定允许通过强制转换不相关的指针类型来做同样的事情。这样做可能会违反别名规则

6.5表达式
7对象的存储值只能由具有以下类型之一的左值表达式访问:

  • 与对象的有效类型兼容的类型,
  • 与对象的有效类型兼容的类型的限定版本,
  • 是与对象的有效类型相对应的有符号或无符号类型的类型,
  • 作为与对象的有效类型的限定版本相对应的有符号或无符号类型的类型,
  • 在其成员中包括上述类型之一的聚合或联合类型(递归地包括子聚合或包含的联合的成员),或者
  • 字符类型。
    唯一的问题是
((Struct1 *) struct2_ptr)->a_short

构成对整个Struct2对象的访问(在这种情况下,它违反了6.5/7,并且它是未定义的),或者仅仅是对short对象的访问(在这种情况下,它可能是完美定义的)。
一般来说,坚持以下规则可能是个好主意:类型双关可以通过联合实现,但不能通过指针实现。不要通过指针实现,即使你处理的是两个struct类型,它们有一个共同的初始成员子序列。

jutyujz0

jutyujz05#

它很可能会工作。但是你问你如何确定这个代码是有效的是非常正确的。所以:在程序某处(可能在启动时)嵌入一堆ASSERT语句,以确保offsetof( Struct1.a_short )等于offsetof( Struct2.a_short )等。此外,您以外的程序员可能有一天修改了其中一个结构,但没有修改另一个结构,所以安全总比遗憾好。

eivgtgni

eivgtgni6#

是的,这样做是可以的!
示例程序如下所示。

#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;
}

相关问题