C 语言中给包含不同类型的 union 进行初始化

116 阅读2分钟

今天在做编译器测试的时候遇到了一个关于union的赋值问题,在这里分享给大家

先写两个代码的例子

#include <stdio.h>

int main() {
  // 对 union 做初始化,不指定成员
  union _uni {
    int a;
    float b;
  } _u = {0x34};
  
  // 给出这这两个数据类型的内存表示
  float f = 0x34;
  int i = 0x34;
  
  // 打印内存表示
  printf("%x\n", *(int*)&(_u.a));
  printf("%x\n", *(int*)&(_u.b));
  printf("%x\n", *(int*)&(f));
  printf("%x\n", *(int*)&(i));

  return 0;
}

输出:
34
34
42500000
34
#include <stdio.h>

int main() {
  // 在这里调换了成员顺序
  union _uni {
    float b;
    int a;
  } _u = {0x34};

  float f = 0x34;
  int i = 0x34;

 
  printf("%x\n", *(int*)&(_u.a));
  printf("%x\n", *(int*)&(_u.b));
  printf("%x\n", *(int*)&(f));
  printf("%x\n", *(int*)&(i));

  return 0;
}

输出:
42500000
42500000
42500000
34
  1. 通过指针转换为 int*, printf 可以输出内部 4B 内存十六进制表示

  2. 问题出现在 union 的赋值方式,存入0x34是按照 float 去存储还是 int 呢?实验结果看来是按照成员的顺序,按照排在前面的成员进行优先存储。

  3. 42500000 表示什么意思呢?0x34 = 3*16+4 = 52,float 的内存表示是 1 符号位 8 指数位 23 尾数位,而 52 = 0b110100 = 1.101 * 2^5 ,符号位为 0,指数位为 127(偏移量) + 5 = 128 +4 = 1000 0100,尾数为 101,所以二进制表示应该为 0b0100 0010 0101 0000 0000 0000 0000 0000 = 0x42500000

  4. C 的规范里面怎么规定的呢?这个行为是编译器指定的(Implementation-defined behavior),还是C语言规范中指定的呢(Well-defined behavior)?答案是C语言规范中已经规定好了,在C99规范中可以找到明确规定,是Well-defined Behavior

    When no designations are present, subobjects of the current object are initialized in order according to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union

    当没有明确指定成员的时候,这些对象的子对象(成员)会根据他们的类型按照一定顺序进行初始化:array 按照下标顺序,struct 按照声明顺序,union 用第一个有名字的成员进行初始化

综上,union 类型在进行不指定成员的初始化的时候,是按照第一个有名字成员的内存表示进行储存的,其余的类型会按照这个内存表示进行解码。