-1

A rather simple code:

#include <stdio.h>
  
union foo {
  int a;
  float b;
};

int main() {
  union foo bar;
  bar.a = 10;

  printf("Addresses: %p %p\n", &(bar.a), &(bar.b));
  printf("Values: %d %f\n", bar.a, bar.b);

  return 0;
}

The result:

Addresses: 0x7ffe8eda48a4 0x7ffe8eda48a4
Values: 10 0.000000

The addresses are the same for the two Union members (like it should be). But shouldn't this mean that the value at those address will also be the same - which doesn't seem to be the case?

Thanks :)

Mihir Chaturvedi
  • 266
  • 1
  • 5
  • 12
  • 1
    You're interpreting the same data as different types. Why would you expect the values to be the same? – Barmar Oct 15 '20 at 07:43
  • It's only valid to read the last member of a member that was written. – Barmar Oct 15 '20 at 07:43
  • @Barmar I've tried almost all the permutations possible - typecasting the data members, changing the `%f` to `%d`, using pointers, assigning to `bar.b`, etc. I still don't seem to get the same values. Though I do when both the members are of the same type. – Mihir Chaturvedi Oct 15 '20 at 07:46
  • 4
    Why would you expect to get the same values? It doesn't do type conversion, it interprets the contents of the memory as if it's the specified datatype. – Barmar Oct 15 '20 at 07:47
  • Suppose you put inside a box a "red button". Then you ask person `a` the color of the thing in the box and person `b` the type of the thing in the box. It's the same thing, but people will answer differently :) – pmg Oct 15 '20 at 07:49
  • 2
    You need to understand how *floating-point* numbers are stored in IEEE-754 format. Not every integer you enter for `a` will correspond to a valid `float` when read as `b`. – David C. Rankin Oct 15 '20 at 07:50
  • 2
    The two apparently different values you are getting have the same bit pattern. It's just interpreted in different ways. Similarly `'A'` and `65` are different ways of interpreting the bit pattern `01000001`. – Weather Vane Oct 15 '20 at 07:56
  • [What is a subnormal floating point number?](https://stackoverflow.com/q/8341395/3422102) provides a good read on what bit combinations can makeup a valid floating point number. – David C. Rankin Oct 15 '20 at 07:56
  • @DavidC.Rankin and Weather Vane, thank you! My assumption was that the interpretation of the bit patterns can work for float and int, which I now know to be clearly wrong. Thanks for the clarification! – Mihir Chaturvedi Oct 15 '20 at 08:09

1 Answers1

1

If you try to read a union member other than the one that was last written, the results are not well-defined (except in some special cases where the union contains structures with compatible types). The standard says:

If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called ''type punning''). This might be a trap representation.

So if you store into bar.a and then try to read bar.b, it reinterprets the contents of that memory as a float. You don't get the same value because the reprentation of a floating point 10 is completely different from an integer 10.

Casting (int)bar.b won't solve the problem because that will first interpret the contents as a float and then convert that to an integer.

What you can do is use casting of a pointer to the member. However, I think this may violate the strict aliasing rule.

printf("%d %d\n", foo.a, *(int *)&foo.b);
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Thanks a lot for the explanation :). My initial assumption - that the representation of `float` and `int` are _compatible_ - was quite incorrect, now that I understand. – Mihir Chaturvedi Oct 15 '20 at 08:07
  • 1
    See https://en.wikipedia.org/wiki/Single-precision_floating-point_format – Barmar Oct 15 '20 at 08:09
  • 1
    @MihirChaturvedi I'd wager you've more experience with JavaScript, do you? – datenwolf Oct 15 '20 at 08:15
  • @datenwolf haha yep, that's my primarily language – Mihir Chaturvedi Oct 15 '20 at 08:17
  • Describing the reading of a union member other than the last one written as “implementation-defined” is not correct. The C standard does not specify implementation-defined behavior except that the implementation must define it, but the reinterpretation of the union contents as the new type is constrained by various rules in C 2018 6.2.6 “Representations of types.” Those rules contain implementation-defined aspects, but they are not, in the end, implementation-defined. – Eric Postpischil Oct 15 '20 at 12:05
  • @EricPostpischil I initially assumed it was UB, but this wording doesn't seem to say that, either. What category does it actually fall into? – Barmar Oct 15 '20 at 14:34
  • @Barmar: It is not in a particular category; it is just stuff specified by the C standard that involves a combination of different things. I might say “If you read a union member other than the one that was last written, the union contents are reinterpreted as the new type…” You could mention this **involves** implementation-defined behavior, because it matters how the types are represented, or simply say it is not portable due to the fact that different implementations may represent types differently, et cetera. – Eric Postpischil Oct 15 '20 at 14:40
  • I checked it to "not well-defined". – Barmar Oct 15 '20 at 14:49