这种对工会行为的解释是否准确?

时间:2016-07-21 03:09:18

标签: c language-lawyer unions c11 strict-aliasing

^^^这个问题不是关于打击类型^^^

据我所知,union中包含的对象只能在它处于活动状态时使用,并且如果它是存储值的最后一个成员则它是活动的。这表明在我标记的点上应该未定义以下代码。

我的问题是,如果我理解何时定义访问工会成员,我是否正确,特别是在以下情况下。

#include <stddef.h>
#include <stdio.h>

void work_with_ints(int* p, size_t k)
{
   size_t i = 1;
   for(;i<k;++i) p[i]=p[i-1];
}

void work_with_floats(float* p, size_t k)
{
   size_t i = 1;
   for(;i<k;++i) p[i]=p[i-1];
}

int main(void)
{

   union{ int I[4]; float F[4]; } u;

   // this is undefined because no member of the union was previously
   // selected by storing a value to the union object
   work_with_ints(u.I,4);
   printf("%d %d %d %d\n",u.I[0],u.I[1],u.I[2],u.I[3]);

   u.I[0]=1; u.I[1]=2; u.I[2]=3; u.I[3]=4;

   // this is undefined because u currently stores an object of type int[4]
   work_with_floats(u.F,4);
   printf("%f %f %f %f\n",u.F[0],u.F[1],u.F[2],u.F[3]);

   // this is defined because the assignment makes u store an object of
   // type F[4], which is subsequently accessed
   u.F[0]=42.0;
   work_with_floats(u.F,4);
   printf("%f %f %f %f\n",u.F[0],u.F[1],u.F[2],u.F[3]);

   return 0;
}

我注意到的三个项目是否正确?

由于大小,我的实际示例不可能在这里使用,但是在评论中建议我将此示例扩展为可编译的东西。我在clang(-Weverything -std = c11)和gcc(-pedantic -std = c11)中编译并运行了上述内容。每个人都给出了以下内容:

0 0 0 0
0.000000 0.000000 0.000000 0.000000
42.000000 42.000000 42.000000 42.000000

这似乎是合适的,但这并不意味着代码是合规的。

编辑:

为了澄清代码的作用,我将指出应用第一段中提到的属性的确切实例。

首先,读取并修改未初始化的联合的内容。如果我在第一段中提到的原则是真的,那么这是未定义的行为,而不是未指定具有陷阱的UB的可能性。

其次,union的内容与非活动union成员的类型一起使用。同样,如果我在第一段中提到的原则是真的,那么这是未定义的行为,而不是未指定具有陷阱的UB的可能性。

第三,如果修改了非活动成员中包含的数组的第一个一个元素,那么刚才提到的“第二个”项会产生具有陷阱的UB潜在的未指定行为。这使得整个数组成为活动成员,因此定义性发生了变化。

我在这个问题的第一段中展示了该原则的后果,以表明该原则如果正确,将如何影响C标准的性质。由于在某些情况下对标准的性质产生了重大影响,我正在寻求帮助,以确定我所说的原则是否正确理解标准。

编辑:

我认为这可能有助于描述我如何从标准中得出上面第一段中的原则,以及人们如何不同意。标准中关于这个问题的说法不多,所以无论如何都必须填补空白。

该标准将联合描述为一次持有一个对象。这似乎建议将其视为包含一个元素的结构。似乎任何偏离这种解释的东西值得一提。这就是我如何达到我所说的原则。

另一方面,有效类型的讨论没有定义术语“声明类型”。如果该术语被理解为联合成员没有声明的类型,则可以认为联合的每个子对象都需要递归地解释为另一个成员。因此,在我的代码的最后一个例子中,所有浮点数组成员都需要初始化,而不仅仅是第一个。

我给出的未定义行为的两个例子对我来说很重要。但是,最后一个与上一段有关的例子似乎是最重要的。我真的可以在那里看到一个争论。

编辑:

这不是一个打字问题。首先,我在谈论写作工会,而不是从他们那里读书。其次,我在谈论用指针而不是联合类型进行这些写操作的有效性。这与打字问题非常不同。

这个问题与严格别名有关,而不是类型惩罚。由于严格的别名,您无法访问所需的内存。这个问题确切地讨论了联盟如何缓解严格别名对其成员的限制。不是说他们曾经这样做过,但如果他们不这样做那么你就永远不会做下面的事情。

union{int i} u; u.i=0; function_working_with_an_int_pointer (&u.i);

因此,显然,在某些情况下,工会会影响严格别名规则的应用。我的问题是确认根据我对标准的阅读而绘制的线是正确的。

1 个答案:

答案 0 :(得分:0)

  

联合中包含的对象只有在它处于活动状态时才能使用,并且如果它是存储值的最后一个成员则它是活动的。

该陈述是错误的。行为是可靠和明确的。

offsetTop

访问 union { unsigned char c [4]; long d; } v; v .d = 0xaabbccddL; printf ("%x\n", v .c [2]); 成员完全可以接受,即使它不是最后一个成员。在小端机器上,它肯定会显示c并在大端机器bb上显示。