联盟使用和凌乱的代码

时间:2013-10-02 12:27:57

标签: c++ gcc unions

我有一些关于union的用法的代码,如下所示:

int main(){

  typedef union{int a;char b[10];float c;}Union;

  Union x,y = {100};
  printf("Union x :%d|   |%s|   |%f \n",x.a,x.b,x.c );
  printf("Union y :%d|   |%s|   |%f \n\n",y.a,y.b,y.c);

  x.a = 50;
  printf("Union x :%d|   |%s|   |%f \n",x.a,x.b,x.c );
  printf("Union y :%d|   |%s|   |%f \n\n",y.a,y.b,y.c);

  strcpy(x.b,"hello");
  printf("Union x :%d|   |%s|   |%f \n",x.a,x.b,x.c );
  printf("Union y :%d|   |%s|   |%f \n\n",y.a,y.b,y.c);

  x.c = 21.50;
  printf("Union x :%d|   |%s|   |%f \n",x.a,x.b,x.c );
  printf("Union y :%d|   |%s|   |%f \n\n",y.a,y.b,y.c);

  return 0;
}

在编译并执行上面的代码之后,我得到了这样的结果:

  Union x :0|     ||    |0.000000 
  Union y :100|   |d|   |0.000000 

  Union x :50|    |2|   |0.000000 
  Union y :100|   |d|   |0.000000 

  Union x :1819043176|     |hello|      |1143141483620823940762435584.000000 
  Union y :100|   |d|   |0.000000 

  Union x :1101791232|   ||    |21.500000 
  Union y :100|   |d|   |0.000000 

我不知道为什么y.b被初始化为“d”?为什么x.a和x.c的值在之后改变了?为什么strcpy(x.b,“你好”)不起作用?

4 个答案:

答案 0 :(得分:3)

严格来说,标准的 type-punning 在狭窄的情况下是undefined behavior,但实际上很多编译器都支持它,例如gcc manual points here for type-punning -fstrict-aliasing 部分说:

  

从不同的工会成员阅读的做法比最近写的那个(称为“打字式”)很常见。即使使用-fstrict-aliasing,只要通过union类型访问内存,就允许类型为punning。

如果您计划大量使用 type-punning ,我建议您阅读Understanding Strict Aliasing

y.b的值为b,因为联合的所有元素共享内存并且您使用y初始化100,其中 ASCII b。这就是为什么x的其他字段在修改strcpy的情况下会发生变化的原因,包括9.5的情况,并且取决于您的编译器,这可能是未定义的或定义良好的(在gcc的情况下它被定义)。

为了完整起见,C ++草案标准部分{{1}} 联盟 1 表示(强调我的):

  

在联合中,最多一个非静态数据成员可以随时处于活动状态,也就是说,最多可以存储一个非静态数据成员的值在任何时候的工会。 [注意:为了简化联合的使用,我们做了一个特别的保证:如果标准布局联合包含几个共享一个共同初始序列(9.2)的标准布局结构,那么如果是这个标准的对象 - layout union type包含一个标准布局结构,允许检查任何标准布局结构成员的公共初始序列;见9.2。 -end note] union的大小足以包含其最大的非静态数据成员。每个非静态数据成员都被分配,就像它是结构的唯一成员一样。

答案 1 :(得分:2)

如果您看到ASCII table,则会看到值100与字符'd'相同。

您必须记住,联盟的所有成员共享相同的内存。这意味着如果您设置了一个联盟的一个成员,所有成员都会更改,而不是总是可以理解的。因此,写入一个成员并从另一个成员读取是未定义的行为。

并且strcpy(x.b,"hello")确实有效,因为您可以看到x联盟的所有成员在您这样做后发生了变化。

答案 2 :(得分:2)

您的代码有UB(未定义的行为);分配给一个工会的一个成员然后检查另一个成员是非法的(除了具有相同初始成员的POD结构的特殊情况)。

在分配到x之前,读取未初始化的值(例如x.a的任何字段)也是非法的。

答案 3 :(得分:-2)

我认为当你说int时,你会分配sizeof(int),一般是32位。 但是当你说char [10]时,你正在分配10 * sizeof(char)字节!! (10 * 4位) 这两者不能相互融合。