Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Structures inside union

Tags:

c

struct

unions

int main()
{
  union {
    struct {
      char c[2];
      char ch[2]; 
    } s;
    struct {
       int i;
       int j; 
    } st;
  } u = { (12, 1), (15, 1) };

  printf("%d %d ", u.st.i, u.st.j);
}

How come the above is printing "257 0"?

What difference is created by using {} instead of ()?

like image 368
user1915016 Avatar asked Dec 19 '12 07:12

user1915016


People also ask

Can we use a structure inside a union?

A structure can be nested inside a union and it is called union of structures. It is possible to create a union inside a structure.

What is structure within union?

A structure contains an ordered group of data objects. Unlike the elements of an array, the data objects within a structure can have varied data types. Each data object in a structure is a member or field. A union is an object similar to a structure except that all of its members start at the same location in memory.


2 Answers

{} signifies initializing a sub-object.

() is an operator that groups an expression, e.g. (1+3)*2. You have confusingly used the comma operator, which discards its left-hand operand and returns the right-hand operand. (12,1) is the same as 1.

Initializing a union always sets its first member and ignores the others. This is because only one member at a time can store a value.

Initializing an array subobject with a scalar value, as in passing 1 to initialize c[2], automatically jumps into the array. This is called brace elision. The next 1 will initialize the second member of the array.

You assigned 1 to each of the chars in c[2], and then read back the resulting byte string as a little-endian int. The array ch[2] was not explicitly initialized at all; in C++ it would be set to zero but I'm not entirely sure in C.

The initializer { {12, 1}, {15, 1} } doesn't work because, apparently, brace elision interprets the first } to close the entire union.

The initializer {{ {12, 1}, {15, 1} }} would avoid brace elision and set both arrays. { 12, 1, 15, 1 } should do the same.

Note that the conversion between scalar values and byte strings is implementation-defined; in particular it depends on endianness and the size of int.

like image 167
Potatoswatter Avatar answered Oct 20 '22 13:10

Potatoswatter


Both (12, 1) and (15, 1) simplify to (oddly enough) 1. This is because, as Omkant said, you're using the comma operator, which executes each expression it divides, but returns the value of the final expression. The Wikipedia entry actually explains this pretty well.

As a result, u.s.c[0] gets filled with the first 1 and u.s.c[1] gets filled with the second 1. Since a union overlays int u.st.i over u.c[2] and u.ch[2] (assuming 8-bit chars and 32-bit ints), and the architecture is little-endian (known from your result), you have a 1 in the lowest byte of u.st.i and a 1 in its second lowest byte, for a value of 256*1 + 1 = 257.

Meanwhile, no values were written to the memory of u.st.j, so the 2nd output is 0.

like image 39
David Duncan Avatar answered Oct 20 '22 12:10

David Duncan