3

I was just reading up on the theory behind unions and stacks. The book (Object Orientated Programming with C++ by E Balagurusamy) said that "members of a union can only be manipulated one at a time". But I was messing around with unions. I did this with no error.

#include <iostream>
#include <iomanip>
#include "ConsoleApplication1.h"

using namespace std;
//user defined data types
#define writeln(x)(cout<<x<<endl)
union result
{
    int marks;
    char grade;
    float percent;
};
int main()
{  
    result result;
    result.marks = 90;
    result.grade = 'a';
    writeln(result.grade);
    writeln(result.marks);
}

So may you please clarify what that statement meant. Thanks:).

  • 2
    See also https://stackoverflow.com/questions/11373203/accessing-inactive-union-member-and-undefined-behavior – Max Langhof Sep 23 '19 at 08:07
  • 1
    In short, some compilers allow you to do this (mainly because it is legal in C) but the C++ standard explicitly makes it Undefined Behavior to read from a different union member than the one that was last written to ("is active"). – Max Langhof Sep 23 '19 at 08:16
  • In C++, that you can do something does not mean that it's valid or permitted. You *can* drive 100 mph through a school zone, but you will not convince any judge that it means that you did nothing wrong. – molbdnilo Sep 23 '19 at 08:35

3 Answers3

6

It means that you are invoking Undefined Behaviour. Let us see what happen of each line of code:

result result;         // ok, you have declared an union
result.marks = 90;     // ok, result.marks is defined
result.grade = 'a';    // ok, result.grade is defined, but result.mark is no longer
writeln(result.grade); // ok, access to the last written member of the union
writeln(result.marks); // UB: access to a member which is not the last writter

UB is really unfriendly for newcomers, because anything can happen:

  • the compiler could detect the problem and raise a warning or error (but it is not required to...)
  • writeln(result.marks) could write 90 or the code of the 'a'character (97) or nothing, or something else of even end the program

And as anything can happen, you can get the expected behaviour on one run, and later get a different one.

Long story made short: do not play with that...

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
1
  • result result;

  • result.marks = 90;

    // wite 4 byte start from &result(memory : 5a 00 00 00 - 90) NOTE: memory used mechanism big endian or little endian 90 is 00 00 00 5a, big endian memory will 5a 00 00 00 and little endian will opposite memory when assign marks

  • result.grade = 'a';

    // wite 1 byte start from &result (memory : 61 - 'a') memory when assign grade

  • writeln(result.grade); // get 1 byte start at &result

  • writeln(result.marks); // get 4 byte start at &result

Community
  • 1
  • 1
Tu Dao Anh Tu
  • 103
  • 1
  • 8
  • This is a nice and detailed answer. Simply it ignores the fact that the compiler is not required to access the memory. Per the *strict aliasing rule* the compiler is allowed to expect that `result.marks` has not been changed, so it could keep the `90` value in a register and use it with no memory access. That is the reason why UB must be avoided, even if a specific run on a specific platform gives the expected result. – Serge Ballesta Sep 26 '19 at 05:56
0

In a union every variable shares the same memory. For that reason you get the outputs 'a' and '97'.

result result;
result.marks = 90; // memory value is 90
result.grade = 'a'; // memory value is 97 ('a')
writeln(result.grade); // print 97 ('a')
writeln(result.marks); // print 97
Fade
  • 85
  • 11
  • what about most likely `sizeof(int) != sizeof(char)`? – fas Sep 23 '19 at 08:12
  • @user3365922 You are right. As the post linked by Max Langhof states it is undefined behaviour. I just wanted to clarify what union does. – Fade Sep 23 '19 at 08:16