Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast struct pointer to another struct

Tags:

c

casting

struct

This code snippet prints the value 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(void)
{
    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 }.

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. Instead, I'm providing it a pointer to a struct of two data elements of data types struct A, int.

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.

Thus, when I pass in a to printA, the line a->x is performed, essentially asking to access the first data element of 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.

What exactly is happening here?

like image 604
Hatefiend Avatar asked Dec 02 '25 05:12

Hatefiend


1 Answers

The language-lawyer explanation of why the code is fine:

  • Any pointer in C may be converted to any other pointer type. (C17 6.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.
  • 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:

    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. &b is suitably converted to the correct type.

  • There is no violation of "strict aliasing", since we fulfil C17 6.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. The lvalue access that happens inside the print function is fine. 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.

like image 91
Lundin Avatar answered Dec 03 '25 17:12

Lundin