3

I have two struct types

typedef struct {
    int i;
    int j;
} struct_a;

typedef struct {
    int i;
} struct_b;

where struct_a and struct_b obviously share a common initial sequence. Based on these structs I have the following snippet in which I convert between struct_a and struct_b by casting and dereferencing pointers

struct_a a = { 0, 1 };
printf("%i %i\n", a.i, a.j);

struct_b b = *((struct_b *)&a);
printf("%i\n", b.i);

struct_a c = *((struct_a *)&b);
printf("%i %i\n", c.i, c.j);

The output of the above code after compiling with gcc is

0 1
0
0 0 <= ?

My question is why does c lose the value of j that was specified when initializing a? Is it because dereferencing the pointers, for instance, in

struct_b b = *((struct_b *)&a);

effectively copies the struct?

hennes
  • 9,147
  • 4
  • 43
  • 63
  • Because you're copying the dereference of the pointer. You aren't storing the pointer. – RamblingMad Sep 06 '15 at 11:32
  • 1
    Yes; `a` and `b` occupy separate addresses. In dereferencing `(struct_b *)&a`, you're only copying `sizeof(struct_b)` bytes into `b` and the rest is lost. – Will Vousden Sep 06 '15 at 11:32
  • "where struct_b obviously is a subtype of struct_a" – no, conversely. – The Paramagnetic Croissant Sep 06 '15 at 11:39
  • "obviously is a subtype". Obviously C doesn't have subtypes, but if you are going to apply this notion to C, then it's the other way around: struct_a is a subtype of struct_b. – n. m. could be an AI Sep 06 '15 at 11:41
  • By casting one `struct` pointer to the type of another and then de-referencing it, you're breaking C's "strict aliasing rules" (see: http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) and the behavior is undefined. After that point, pretty much anything goes including the apparent "information loss" that you've observed; although you've also copied from a `struct_b` to a `struct_a` object, which is where the loss really comes from in this case. The assignment to `struct_b b` only assigns the fields of `struct_b`, naturally enough. – davmac Sep 06 '15 at 12:02
  • @davmac here strict aliasing is not violated because a and b share a common initial sequence. [See this](https://stackoverflow.com/questions/3846551/structures-and-casting-in-c). – The Paramagnetic Croissant Sep 06 '15 at 12:13
  • @TheParamagneticCroissant the "common initial sequence" clause is for access to structs as members of a _union_. It is not a general license to assume that structs with a common initial sequence may partially alias. – davmac Sep 06 '15 at 12:15
  • @TheParamagneticCroissant (and anyway, the "common initial sequence" clause only allows access to that initial sequence. The code in question access a `struct_b` object via a `struct_a` pointer and tries to read the whole `struct_a` object, thus exceeding the initial sequence). – davmac Sep 06 '15 at 13:20
  • @TheParamagneticCroissant / @n.m.: Sorry I meant `struct_b` is a subset of `struct_a` in the sense that they have the same initial sequence. Subtype is obviously the wrong word here. Thanks for catching that sloppiness. – hennes Sep 06 '15 at 13:36

2 Answers2

1

Yes, you are copying. When copying a to b, there is only room for one value. The other one is lost.

And the cast in the last assignment is not allowed, so you could have any result from that.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
1

Casting and dereferencing struct pointers does not lose information.

Some notes about your code:

  • a, b and c are different locations in memory.
  • c.j has not been initialized in the code, its value could be anything.

Casting the type back and forth for the same variable produces the expected result:

struct_a c = *((struct_a *)((struct_b *)&a)); 
printf("%i %i\n", c.i, c.j);

Another option that produces the expected result:

struct_a a = { 0, 1 };
printf("%i %i\n", a.i, a.j);

struct_b *b = (struct_b *)&a; // *b points a
printf("%i\n", b->i);

struct_a c = *((struct_a *)b); // c is a copy of a
printf("%i %i\n", c.i, c.j);
Community
  • 1
  • 1
sergej
  • 17,147
  • 6
  • 52
  • 89