【问题标题】:Are pointers to union required to be aligned for all members是否需要为所有成员对齐指向联合的指针
【发布时间】:2015-05-12 04:41:00
【问题描述】:

给定

typedef union { unsigned char b; long l; } BYTE_OR_LONG;

拥有一个函数是否合法

unsigned long get_byte_or_long(BYTE_OR_LONG *it)
{
  if (it->b)
    return it->b;
  else
    return decode_long(it->l); // Platform-dependent method
                               // Could return (it), (it>>8), etc.
}

然后调用它

void test()
{
  long l = encode_long(12345678);  // Platform-dependent; could return
                                   // (it<<8), (it & 16777215), etc.
  char b[2] = {12,34};
  BYTE_OR_LONG *bl[3];
  bl[0] = (BYTE_OR_LONG*)&l;
  bl[1] = (BYTE_OR_LONG*)b;
  bl[2] = (BYTE_OR_LONG*)(b+1);
  for (int i=0; i<3; i++)
    printf("%lu\n", get_byte_or_long(bl[i]));
}

当然构造一个未对齐的BYTE_OR_LONG *p 然后访问p-&gt;l 将是未定义的行为。此外,即使将未对齐的指针强制转换为(unsigned long*) 的行为也会是未定义行为,因为对于这种类型的实现可能不需要像char* 那样多的位。然而,有了工会,事情似乎还不清楚。

据我了解,指向联合的指针应该等同于指向其任何元素的指针。这是否意味着保证指向联合类型的指针所需的实现必须能够识别其中包含的任何类型的任何实例[因此BYTE_OR_LONG* 必须能够识别任何unsigned char],还是需要程序员只转换为满足其中每个组成部分的每个对齐要求的联合类型指针?

【问题讨论】:

  • OT:` 应该是printf("%p\n", (void*) bl[i]); 而不是printf("%lu\n", bl[i]);
  • @alk:代码在一个重要细节上是错误的——它从未将bl[i] 传递给将要使用它的代码。感谢您的评论-我修复了上面的代码。

标签: c alignment unions


【解决方案1】:

指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未正确对齐引用类型 [...],则行为未定义。 [C11 (n1570) 6.3.2.3 p7]

我找不到任何关于联合对齐要求的明确保证,因此到联合指针的转换似乎不是严格符合的。在我的机器上,_Alignof(char) 是 1,但 _Alignof(BYTE_OR_LONG) 是 4。

这是否意味着保证指向联合类型的指针必须能够识别其中包含的任何类型的任何实例所需的实现[因此BYTE_OR_LONG* 必须能够识别任何无符号字符] ,还是要求程序员只转换为满足其中每个组成部分的每个对齐要求的联合类型指针?

不,指向T 的指针可以指向任何包含T 的联合,不一定反过来。据我所知,工会的结盟要求甚至可能比其所有成员的要求更严格。

【讨论】:

  • 我希望_Alignof(someUnion) 会报告允许使用联合的每个成员的对齐方式。问题本质上是一个人是否可以合法地创建一个联合类型的指针,它满足一些但不是所有成员的对齐要求,如果只有满足对齐要求的成员被访问。
  • @supercat:正如我所说,只要将指针转换为与其目标错误对齐的指针,就会违反对齐要求。你是否取消引用它并不重要。如果你想假设一个指向未对齐地址的指针不是问题的平台(但通过这样的指针进行访问),我认为代码是定义的。
  • ...虽然它在技术上仍然会违反严格的别名(或者可能不会),但这是 C11 标准 afaik 中当前未解决的问题。
  • 我认为这很可能,但我很好奇标准中是否有任何内容可以澄清。从概念上讲,如果一个方法接受一个指针,如果它指向一种数据则必须对齐,但如果它指向不同的类型则不需要对齐,那么让它接受一个可以传达该消息的指针类型似乎很好对程序员来说,但如果标准不允许这样做,那么接受字节对齐类型似乎会更好。
  • 我也可以看到一些关于严格别名的有趣问题。作为一个常见的现实世界场景,给定typedef union {uint8_t bb[4]; uint32_t l;} QUAD;,编译器是否需要假设如果uint32_t 的地址可能已转换为QUAD*,则接受QUAD* 的方法可能会影响@ 987654331@?后一种情况似乎很常见。
【解决方案2】:

这是否意味着需要保证指向联合类型的指针的实现必须能够识别其中包含的任何类型的任何实例...?

长问题,短答案:是的。

(稍后我会挖掘标准参考)

基本上这是因为结构/联合的第一个元素保证在它之前没有填充。

【讨论】:

  • 如果有一个foo 类型为unsigned char* 的变量,那么任何unsigned char* 类型的变量都可以将其地址存储在foo 中,这样*foo 就会识别多变的。问题是,对于每个unsigned char 类型的变量,是否可以创建BYTE_OR_LONG* 类型的foo2 变量,以便foo2-&gt;b 可以识别该变量,或者是否只能为unsigned char 创建这样的指针恰好位于字对齐地址的变量。
  • 我似乎不明白这个问题。 foo2-&gt;b 变量。它如何识别另一个变量foo? @supercat
  • 假设“此类指针”指的是类型为BYTE_OR_LONG* 的指针:是的,它的成员bs 都会像l 这样的类型一样对齐,即是 longs 。 @supercat
  • 好吧,我终于明白了。我将删除我的答案。 @supercat
  • 你不可乱投。 ;-) @supercat
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-24
  • 2012-02-26
  • 1970-01-01
相关资源
最近更新 更多