繁体   English   中英

将struct指针转换为另一个struct

[英]Cast struct pointer to another struct

此代码段打印值5 我不明白为什么。

#include <stdio.h>

struct A
{
    int x;
};

struct B
{
    struct A a;
    int y;
};

void printA(struct A *a)
{
    printf("A obj: %d\n", a->x);
}

int main()
{
    struct B b = {
        {
            5
        },
        10
    };

    struct A *a = (struct A*)&b;
    printA(a);

    printf("Done.\n");

    return 0;
}

当我创建b ,指向它的指针将指向数据{ {5}, 10 }

当我将&bstruct A* ,我向编译器保证这个struct A*指向数据类型为int的单个数据元素的结构。 相反,我提供了一个指向数据类型struct Aint的两个数据元素的结构的指针。

即使第二个变量被忽略(因为struct A只有一个数据成员),我仍然提供一个结构,其成员的数据类型为struct A ,而不是int

因此,当我通过在aprintA ,线a->x进行,基本上,要求访问的第一数据元素a 的第一数据元素a的数据类型的struct A ,这是一种类型不匹配,由于%d期待一个数字,而不是一个struct A

到底发生了什么?

当我创建b ,指向它的指针将指向数据{ {5}, 10 }

是的,从某种意义上讲,它是适合类型且价值正确的C初始化程序的文本。 这文字本身不应该按字面意思理解为结构的价值。

当我将&bstruct A* ,我向编译器保证这个struct A*指向数据类型为int的单个数据元素的结构。

不,不完全是。 您正在表达式&b的值转换为类型struct A * 结果指针实际指向struct A是一个单独的问题。

相反,我提供了一个指向数据类型struct Aint的两个数据元素的结构的指针。

不,不是“反而”。 鉴于struct B的第一个成员是struct A ,并且C禁止在结构的第一个成员之前填充,指向struct B的指针指向struct A - B的第一个成员 - 在一般意义上。 正如@EricPostpischi在注释中观察到的那样,C标准在您的特定情况下明确指定结果:给定struct B b ,将指针b转换为类型struct A *产生指向b的第一个成员的struct A ,即struct A

即使第二个变量被忽略(因为struct A只有一个数据成员),我仍然提供一个结构,其成员的数据类型为struct A ,而不是int

struct B表示的第一个sizeof(struct A)字节构成其第一个成员的表示形式,即struct A 后者是前者的成员除了在记忆中的重叠之外没有任何物理表现。

即使语言没有明确指定它,给定你的变量b作为struct B声明,也没有实际的理由期望表达式(struct A*)&b == &b.a将评估为false,并且毫无疑问,右手指针可用于访问struct A

因此,当我通过在一个给printA ,线a->x进行,基本上,要求访问的第一数据元素a

是的,这就是一个断言进入那a确实指向一个struct A 正如您已经讨论的那样,它在您的情况下做了什

的第一数据元素a是数据类型的struct A

根据定义, *astruct A 具体来说,它是struct A其表示与b的表示的开头重叠。 如果没有这样的struct A那么行为将是未定义的,但这不是问题。 像每个struct A ,它有一个由x指定的成员,它是一个int

这是一种类型不匹配,因为%d期望数字,而不是struct A

你的意思是期待一个int 这就是它的结果。 这就是表达式a->x读取的内容,假设行为是完全定义的,因为这是该表达式的类型。 在不同的情况下,行为可能确实没有定义,但在任何情况下,该表达式都不会提供struct A

到底发生了什么?

似乎正在发生的事情是你正在想象比C实际提供的更高级别的语义。 特别是,您似乎将结构的心智模型作为可区分成员对象的列表,这会导致您形成不正确的期望。

也许您更熟悉一种弱类型语言,如Perl,或动态类型语言,如Python,但C的工作方式不同。 您无法查看C对象并且有用地询问“您的类型是什么”? 相反,您通过用于访问它的表达式的静态类型的镜头来查看每个对象。

语言 - 律师解释为什么代码是好的:

  • C中的任何指针都可以转换为任何其他指针类型。 (C176.3.2§7)。
  • 如果在转换后取消引用指向对象是安全的,则取决于:1)如果类型是兼容的,从而正确对齐,2)如果允许使用的相应指针类型为别名。
  • 作为一种特殊情况,指向结构类型的指针等效于指向其第一个成员的指针。 C176.7.2§15的相关部分说:

    指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。

  • 这意味着(struct A*)&b很好。 &b适当地转换为正确的类型。

  • 没有违反“严格别名”,因为我们符合C176.5§7:

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

    • 与对象的有效类型兼容的类型,...
    • 聚合或联合类型,包括其成员中的上述类型之一

    初始成员的有效类型是struct A 在print函数中发生的左值访问很好。 struct B也是一种聚合类型,在其成员中包含struct A ,因此无论顶部引用的初始成员规则如何,都不可能发生严格的别名冲突。

对于这种情况,C标准中有一条特殊规则。 C 2011 6.7.2.1 15说:

指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM