Home > Mobile >  How values are changed in C unions?
How values are changed in C unions?

Time:12-01

#include <stdio.h>

int main()
{
 typedef union{
     int a ;
     char c;
     float f;
 } myu;
 
 myu sam;
 sam.a = 10;
 sam.f=(float)5.99;
  sam.c= 'H';

 printf("%d\n %c\n %f\n",sam.a,sam.c,sam.f);
 return 0;
}

Output

1086303816

H

5.990025

How come the value of integer has changed so drastically while the float is almost the same.

CodePudding user response:

The fields of a union all share the same starting memory address. This means that writing to one member will overwrite the contents of another.

When you write one member and then read a different member, the representation of the written member (i.e. how it is laid out in memory) is reinterpreted as the representation of the read member. Integers and floating point types have very different representations, so it makes sense that reading a float as though it were an int can vary greatly.

Things become even more complicated if the two types are not the same size. If a smaller field is written and a larger field is read, the excess bytes might not have event been initialized.

In your example, you first write the value 10 to the int member. Then you write the value 5.99 to the float member. Assuming int and float are both 4 bytes in length, all of the bytes used by the int member are overwritten by the float member.

When you then change the char member, this only changes the first byte. Assuming a float is represented in little-endian IEEE754, this changes just the low-order byte of the mantissa, so only the digits furthest to the right are affected.

CodePudding user response:

Try this. Instead of using printf (which will mainly output nonesens) show the raw memory after each modification.

The code below assumes that int and float are 32 bit types and that your compiler does not add padding bytes in this union.

#include <string.h>
#include <stdio.h>
#include <assert.h>

void showmemory(void* myu)
{
  unsigned char memory[4];
  memcpy(memory, myu, 4);

  for (int i = 0; i < 4; i  )
  {
    printf("x ", memory[i]);
  }
  printf("\n");
}

int main()
{
  typedef union {
    int a;
    char c;
    float f;
  } myu;

  assert(sizeof(myu) == 4);  // assume size of the union is 4 bytes

  myu sam;
  sam.a = 10;
  showmemory(&sam);

  sam.f = (float)5.99;
  showmemory(&sam);
 
  sam.c = 'H';
  showmemory(&sam);
}

Possible output on a little endian system:

0a 00 00 00      // 0a is 10 in hexadecimal
14 ae bf 40      // 5.99 in float
48 ae bf 40      // 48 is 'H'

CodePudding user response:

How come the value of integer has changed so drastically while the float is almost the same.

That is just a coincidence. Your union will be stored in 4 bytes. When you assign the field "a" to 10, the binary representation of the union is 0x0000000A. And then, when you assign the field f to 5.99, it becomes 0x40bfae14. Finally, you set the c to 'H' (0x48 in Hex), it will overwrite the first byte, which corresponds to the mantissa of the float value. Thus, the float part changes slightly. For more information about floating-point encoding, you can check this handy website out.

CodePudding user response:

In "traditional" C, any object that are not bitfields and do not have a register storage class will represent an association between a sequence of consecutive bytes somewhere in memory and a means of reading or writing values of the object's type. Storing a value into an object of type T will convert the value into a a pattern of sizeof (T) * CHAR_BIT bits, and store that pattern into the associated memory. Reading an object of type T will read the sizeof (T) * CHAR_BIT bits from the object's associated storage and convert that bit pattern into a value of type T, without regard for how the underlying storage came to hold that bit pattern.

A union object serves to reserve space for the largest member, and then creates an association between each member and a region of storage that begins at the start of the union object. Any write to a member of a union will affect the appropriate part of the underlying storage, and any read of a union member will interpret whatever happens to be in the underlying storage as a value of its type. This will be true whether the member is accessed directly, or via pointer or array syntax.

The "traditional C" model is really quite simple. The C Standard, however, is much more complicated because the authors wanted to allow implementations to deviate from that behavior when doing so wouldn't interfere with whatever their customers need to do. This in turn has been interpreted by some compiler writers as an invitation to deviate from the traditional behavior without any regard for whether the traditional behavior might be useful in more circumstances than the bare minimums mandated by the Standard.

  •  Tags:  
  • c
  • Related