将struct指针转换为另一个struct

Hat*_*end 2 c struct casting

此代码段打印该值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;
}
Run Code Online (Sandbox Code Playgroud)

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

当我&b转向时struct A*,我向编译器保证这struct A*指向数据类型的单个数据元素的结构int.相反,我提供一个指针数据类型的两个数据元素的结构struct A,int.

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

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

到底发生了什么?

Joh*_*ger 6

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

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

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

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

相反,我提供一个指针数据类型的两个数据元素的结构struct A,int.

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

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

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

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

因此,当我传入a时printA,a->x执行该行,基本上要求访问第一个数据元素a.

是的,这就是断言进入的地方a确实指向a struct A.正如您已经讨论过的,它在您的情况下做了哪些.

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

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

由于%d期望数字而不是a,这是类型不匹配struct A.

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

到底发生了什么?

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

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


Lun*_*din 6

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

  • 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其成员的聚合类型,因此无论在顶部引用的初始成员规则如何,都不可能发生严格的别名违规.