Union Initialization In C Programming and Code Smell

In this blog post, you learn all the important concepts related to union initialization in C programming.  Here we will not explain the concept of the union in C. We just explain the way and issues related to the C union.

Blog posts cover the following topics:

  • How do you initialize a union in C or Union Initialization In C?
  • How many union members can be initialized?
  • Designated Initializers of a union in C?
  • The bug that can be raised with union Initializers.

 

How do you initialize union in C:

Before understanding how to initialize a union let’s understand the meaning of initialization.

In C programming, initialization is the assignment of an initial value for a variable (object). The way to initialize an object depends on the programming language as well as its type, storage class, etc. Typically initialization performs by initializers and initializer lists.

Consider the below example,

static int data1;   // initializes data1 to 0

int data2 = 1;      // initializes data2 to 1

// initializes int arr[4] to 1,3,5,25
int arr[] = { 1, 3, 5, 25 };

 

Now I believe you have a basic understanding of initialization, so let’s come on to our topic initialization of union.

Like other initializers, a union initializer specifies the initial value stored in a union object. When initializing union type object, the initializer must be a non-empty, (until C23) brace-enclosed, comma-separated list of initializers for the members.

The following are the initializers for the union.

1.)   = { expression , ... }

2.) = { designator expression , ... } (since C99)

3.) = { } (since C23)

 

Let’s discuss each union initializer one by one with an example code.

1. initializer-list:

When initializing a union, the initializer list must have only one member (till C23), which initializes the union’s first member.

Let’s take an example for a better understanding.

#include <stdio.h>
union Test
{
    int x;
    char c;
};


int main()
{
    union Test obj = {1};

    printf("%d\n", obj.x);

    return 0;
}

Output: 1

 

2. Designated initializers ( Since C99):

Using the designator we can initialize the specific member of a union. The designator has the form .identifierHere identifier is the name of a member of the given union type.

The following syntax can initialize any member of a union:

union Test
{
    /*
      List of union members

    */
};


//union object
union Test obj = {.any_member = 42 };

 

You can also use the designator with a union with no tag union(named, but not tagged).

union
{
    /* 
      List of union members 
    
    */
} obj = {.any_member = 42 };

 

Consider the below example,

#include <stdio.h>


union Test
{
    int i;
    double d;
};

int main()
{
    //Initialization
    union Test temp = { .d = 4 };

    printf("%f\n", temp.d);

    return 0;
}

Output: 4.000000

 

3. empty initializer (C23):

An empty brace pair ({}) is called an empty initializer and is referred to as empty initialization. It is introduced in C23.

Consider the below example,

#include <stdio.h>

union Test
{
    int i;
    double d;
};

int main()
{
    //empty Initialization C23
    union Test temp = {};

    printf("%d\n", temp.i);

    return 0;
}

Output: 0

 

One question must come to your mind what will be the value Of an object that is initialized with an empty initializer.

Don’t worry C23 has already given the answer to this question.

So, if an object is s initialized with an empty initializer, then:

  • The pointer type is initialized to a null pointer.
  • Integral types are initialized to unsigned zero.
  • Decimal floating types are initialized to positive zero and the quantum exponent is implementation-defined.
  • All elements of arrays, all members of structs, and the first-named members of unions are initialized according to the above rules, recursively, plus all padding bits are initialized to zero.

 

Initialization of anonymous union:

An unnamed member whose type specifier is a union specifier with no tag is called an anonymous union. Its members are also considered to be members of the containing structure or union, keeping their structure or union layout. And this applies recursively if the containing structure or union is also anonymous.

The following example illustrates anonymous unions:

struct Test
{
    union   // anonymous union
    {
        int var1;
        char var2;
    };
    int data;
} obj;

Because members of anonymous unions are considered to be members of the containing structure
or union, struct Test in the following example has three members. We can access the var1 and var2 using the object of struct Test.

Here I am interested in explaining how you can initialize anonymous union members of a structure. Let’s take an example for a better understanding.

#include <stdio.h>

struct Test
{
    union   // anonymous union
    {
        int var1;
        char var2;
    };
    int data;
};

int main()
{
    struct Test obj = {{257}, 128};

    printf("obj.var1 = %d obj.data = %d\n",obj.var1, obj.data);
    
    return 0;
}

Output:

obj.var1 = 257 obj.data = 128

 

Explanation: 

You can see the above example code where members of the anonymous union are behaving like the members of the structure.

 

Code smell:

Let’s see some example C programs that are not a compiler error but can introduce serious issues.

Question-1:

#include <stdio.h>
union Test
{
    char a;
    int b;
};


int main()
{
    union Test obj = {127};

    printf("%d\n", obj.b);

    return 0;
}

Output: Unspecified Behaviour

 

Explanation:

In the above example, we are initializing the first member of the union which means initializing data members ‘a‘. But we are reading back that ‘b‘ which gives you an unspecified value.

Note: 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 (a process sometimes called “type punning”). If the size of the new type is larger than the size of the last-written type, the contents of the excess bytes are unspecified (and maybe a trap representation).

 

Question-2:

#include <stdio.h>

struct Test
{
    int a;
    union   // anonymous union
    {
        char b;
        int c;
    };

};

int main()
{
    struct Test obj = {128, {257}};

    return 0;
}

Output: You will not get desired output.

 

Explanation:

A union initializer list initializes the union’s first member of the union unless a designated initializer is used. Because in the given example the first member of the union is signed char whose range is -128 to 127 and we are trying to initialize it with 257; Overflow will occur because 257 is outside of its range.

 

Recommended Post: