简体   繁体   English

将struct指针转换为另一个struct

[英]Cast struct pointer to another struct

This code snippet prints the value 5 . 此代码段打印值5 I don't understand why. 我不明白为什么。

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

When I create b , a pointer to it would point to the data { {5}, 10 } . 当我创建b ,指向它的指针将指向数据{ {5}, 10 }

When I cast &b to struct A* , I'm assuring the compiler that this struct A* points to a struct of a single data element of data type int . 当我将&bstruct A* ,我向编译器保证这个struct A*指向数据类型为int的单个数据元素的结构。 Instead, I'm providing it a pointer to a struct of two data elements of data types struct A , int . 相反,我提供了一个指向数据类型struct Aint的两个数据元素的结构的指针。

Even if the second variable is ignored (since struct A has only one data member) I am still providing it a struct whose member is of data type struct A , not int . 即使第二个变量被忽略(因为struct A只有一个数据成员),我仍然提供一个结构,其成员的数据类型为struct A ,而不是int

Thus, when I pass in a to printA , the line a->x is performed, essentially asking to access the first data element of a . 因此,当我通过在aprintA ,线a->x进行,基本上,要求访问的第一数据元素a The first data element of a is of data type struct A , which is a type mismatch due to the %d expecting a digit, not a struct A . 的第一数据元素a的数据类型的struct A ,这是一种类型不匹配,由于%d期待一个数字,而不是一个struct A

What exactly is happening here? 到底发生了什么?

When I create b , a pointer to it would point to the data { {5}, 10 } . 当我创建b ,指向它的指针将指向数据{ {5}, 10 }

Yes, in the sense of that being the text of a type-appropriate and value-correct C initializer. 是的,从某种意义上讲,它是适合类型且价值正确的C初始化程序的文本。 That text itself should not be taken literally as the value of the structure. 这文字本身不应该按字面意思理解为结构的价值。

When I cast &b to struct A* , I'm assuring the compiler that this struct A* points to a struct of a single data element of data type int. 当我将&bstruct A* ,我向编译器保证这个struct A*指向数据类型为int的单个数据元素的结构。

No, not exactly. 不,不完全是。 You are converting the value of the expression &b to type struct A * . 您正在表达式&b的值转换为类型struct A * Whether the resulting pointer actually points to a struct A is a separate question. 结果指针实际指向struct A是一个单独的问题。

Instead, I'm providing it a pointer to a struct of two data elements of data types struct A , int . 相反,我提供了一个指向数据类型struct Aint的两个数据元素的结构的指针。

No, not "instead". 不,不是“反而”。 Given that struct B 's first member is a struct A , and that C forbids padding before the first member of a structure, a pointer to a struct B also points to a struct A -- the B's first member -- in a general sense. 鉴于struct B的第一个成员是struct A ,并且C禁止在结构的第一个成员之前填充,指向struct B的指针指向struct A - B的第一个成员 - 在一般意义上。 As @EricPostpischi observed in comments, the C standard explicitly specifies the outcome in your particular case: given struct B b , converting a pointer to b to type struct A * yields a pointer to b 's first member., a struct A . 正如@EricPostpischi在注释中观察到的那样,C标准在您的特定情况下明确指定结果:给定struct B b ,将指针b转换为类型struct A *产生指向b的第一个成员的struct A ,即struct A

Even if the second variable is ignored (since struct A has only one data member) I am still providing it a struct whose member is of data type struct A , not int . 即使第二个变量被忽略(因为struct A只有一个数据成员),我仍然提供一个结构,其成员的数据类型为struct A ,而不是int

The first sizeof(struct A) bytes of the representation of a struct B form the representation of its first member, a struct A . struct B表示的第一个sizeof(struct A)字节构成其第一个成员的表示形式,即struct A That the latter is a member of the former has no physical manifestation other than their overlap in memory. 后者是前者的成员除了在记忆中的重叠之外没有任何物理表现。

Even if the language did not explicitly specify it, given your declaration of variable b as a struct B , there would be no practical reason to expect that the expression (struct A*)&b == &b.a would evaluate to false, and there can be no question that the right-hand pointer can be used to access a struct A . 即使语言没有明确指定它,给定你的变量b作为struct B声明,也没有实际的理由期望表达式(struct A*)&b == &b.a将评估为false,并且毫无疑问,右手指针可用于访问struct A

Thus, when I pass in a to printA , the line a->x is performed, essentially asking to access the first data element of a . 因此,当我通过在一个给printA ,线a->x进行,基本上,要求访问的第一数据元素a

Yes, and this is where an assertion enters that a really does point to a struct A . 是的,这就是一个断言进入那a确实指向一个struct A Which it does in your case, as already discussed. 正如您已经讨论的那样,它在您的情况下做了什

The first data element of a is of data type struct A , 的第一数据元素a是数据类型的struct A

No. *a is by definition a struct A . 根据定义, *astruct A Specifically, it is the struct A whose representation overlaps the beginning of the representation of b . 具体来说,它是struct A其表示与b的表示的开头重叠。 If there were not such a struct A then the behavior would be undefined, but that's not an issue here. 如果没有这样的struct A那么行为将是未定义的,但这不是问题。 Like every struct A , it has a member, designated by x , that is an int . 像每个struct A ,它有一个由x指定的成员,它是一个int

which is a type mismatch due to the %d expecting a digit, not a struct A . 这是一种类型不匹配,因为%d期望数字,而不是struct A

You mean expecting an int . 你的意思是期待一个int And that's what it gets. 这就是它的结果。 That's what the expression a->x reads, supposing the behavior is defined at all, because that is the type of that expression. 这就是表达式a->x读取的内容,假设行为是完全定义的,因为这是该表达式的类型。 Under different circumstances the behavior might indeed not be defined, but under no circumstance does that expression ever provide a struct A . 在不同的情况下,行为可能确实没有定义,但在任何情况下,该表达式都不会提供struct A

What exactly is happening here? 到底发生了什么?

What seems to be happening is that you are imagining different, higher-level semantics than C actually provides. 似乎正在发生的事情是你正在想象比C实际提供的更高级别的语义。 In particular, you seem to have a mental model of structures as lists of distinguishable member objects, and that's leading you to form incorrect expectations. 特别是,您似乎将结构的心智模型作为可区分成员对象的列表,这会导致您形成不正确的期望。

Perhaps you are more familiar with a weakly typed language such as Perl, or a dynamically typed language such as Python, but C works differently. 也许您更熟悉一种弱类型语言,如Perl,或动态类型语言,如Python,但C的工作方式不同。 You cannot look at a C object and usefully ask "what is your type"? 您无法查看C对象并且有用地询问“您的类型是什么”? Instead, you look at each and every object through the lens of the static type of the expression used to access it. 相反,您通过用于访问它的表达式的静态类型的镜头来查看每个对象。

The language-lawyer explanation of why the code is fine: 语言 - 律师解释为什么代码是好的:

  • Any pointer in C may be converted to any other pointer type. C中的任何指针都可以转换为任何其他指针类型。 (C17 6.3.2 §7). (C176.3.2§7)。
  • If it is safe to dereference the pointed-at object after conversion depends on: 1) if the types are compatible and thereby correctly aligned, and 2) if the respective pointer types used are allowed to alias. 如果在转换后取消引用指向对象是安全的,则取决于:1)如果类型是兼容的,从而正确对齐,2)如果允许使用的相应指针类型为别名。
  • As a special case, a pointer to a struct type is equivalent to a pointer to its first member. 作为一种特殊情况,指向结构类型的指针等效于指向其第一个成员的指针。 The relevant part of C17 6.7.2 §15 says: C176.7.2§15的相关部分说:

    A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. 指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。

  • This means that (struct A*)&b is fine. 这意味着(struct A*)&b很好。 &b is suitably converted to the correct type. &b适当地转换为正确的类型。

  • There is no violation of "strict aliasing", since we fulfil C17 6.5 §7: 没有违反“严格别名”,因为我们符合C176.5§7:

    An object shall have its stored value accessed only by an lvalue expression that has one of the following types: 对象的存储值只能由具有以下类型之一的左值表达式访问:

    • a type compatible with the effective type of the object, ... 与对象的有效类型兼容的类型,...
    • an aggregate or union type that includes one of the aforementioned types among its members 聚合或联合类型,包括其成员中的上述类型之一

    The effective type of the initial member being struct A . 初始成员的有效类型是struct A The lvalue access that happens inside the print function is fine. 在print函数中发生的左值访问很好。 struct B is also an aggregate type that includes struct A among its members, so strict aliasing violations are impossible, regardless of the initial member rule cited at the top. struct B也是一种聚合类型,在其成员中包含struct A ,因此无论顶部引用的初始成员规则如何,都不可能发生严格的别名冲突。

There is a special rule in the C standard for this case. 对于这种情况,C标准中有一条特殊规则。 C 2011 6.7.2.1 15 says: C 2011 6.7.2.1 15说:

A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. 指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。

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

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