结构可以为其自己的初始和唯一成员设置别名吗?

时间:2013-06-29 21:36:36

标签: c language-lawyer strict-aliasing

例如,此代码是否有效,还是通过违反别名规则来调用未定义的行为?

int x;
struct s { int i; } y;
x = 1;
y = *(struct s *)&x;
printf("%d\n", y.i);

我的兴趣在于使用基于此的技术来开发一种用于执行别名读取的可移植方法。

更新:这是预期的用例,略有不同,但当且仅当上述内容有效时才有效:

static inline uint32_t read32(const unsigned char *p)
{
    struct a { char r[4]; };
    union b { struct a r; uint32_t x; } tmp;
    tmp.r = *(struct a *)p;
    return tmp.x;
}

GCC根据需要将其编译为单个32位加载,并且它似乎避免了p实际指向char以外的类型时可能发生的别名问题。换句话说,它似乎是GNU C __attribute__((__may_alias__))属性的可移植替代品。但我不确定它是否真的很明确......

5 个答案:

答案 0 :(得分:5)

我相信这仍会违反有效的输入规则。您希望通过该类型的表达式访问未明确声明(或通过动态分配时通过存储隐式声明)的内存位置。

其他答案中引用的部分都不能用于逃避此基本限制。

但是,我相信您的问题有一个解决方案:使用struct a,即使在独立环境中也可以使用__builtin_memcpy()


请注意,这个问题并不像听起来那么清晰。 C11第6.5节第7节告诉我们,通过左值表达式访问对象是可以的,该表达式具有聚合或联合类型,其成员中包含上述类型之一

C99基本原理清楚地说明了这个限制,所以指向聚合的指针和指向其成员之一的指针可能是别名。

我相信能够以第一个例子的方式使用这个漏洞(但不是第二个例子,假设p并不恰好指向实际{{1} }})是一个意想不到的结果,标准只是由于措辞不精确而无法拒绝。

另请注意,如果第一个示例有效,我们基本上可以将结构类型转换为其他名义上键入的语言。除了共同的初始子序列的联合中的结构(即使这样,成员名称也很重要),相同的内存布局也不足以使类型兼容。我相信同样的理由适用于此。

答案 1 :(得分:3)

我在阅读别名规则(C99,6.5p7)时出现了这句话:

  

“包含其中一种上述类型的聚合或联合类型   成员(包括,递归地,子集合或包含的联合的成员),或“

导致我认为它没有违反C别名规则。

但是,它不违反别名规则这一事实并不足以使此代码段有效。由于其他原因,它可能会调用未定义的行为。

(struct s *) &x

不保证指向有效的struct s对象。即使我们假设x的对齐适用于类型为struct的对象,但强制转换后的结果指针可能不会指向足以容纳结构对象的空间(因为结构可能有在最后一个成员之后填充。)

编辑:答案已完全从其初始版本

重新修改

答案 2 :(得分:0)

不确定这是一个正确的答案,但可能发生的事情(在你的第二个例子中)是:

  1. 编译器将struct a定义为8字节对象,在数组中的4个字节之后填充(为什么?因为它可以)。
  2. 然后使用tmp.r = *(struct a *)p;将p视为struct a的地址(即8字节对象)。它尝试将此对象的内容复制到tmp.r,即p所持有的地址的8个字节。但是你只能从那里读取4个字节。
  3. 实现不必复制填充字节,但允许它们这样做。

答案 3 :(得分:0)

在你的第二个例子中

struct a { char r[4]; };

此结构类型可能有一些对齐限制。编译器可能会认为struct a始终是4字节对齐的,例如,它总是可以使用4字节对齐的读取指令,而不需要查看实际地址。您作为p的参数收到的指针read32没有这样的限制,所以

*(struct a*)p;

可能会导致总线错误。

我注意到这种类型的论证是“实际的”。

从标准的角度来看,只要(struct a*)p转换为具有更严格的对齐要求的类型,这就是UB。

答案 4 :(得分:-1)

来自C标准:

  

指向对象或不完整类型的指针可能会转换为指向其他对象的指针   对象或不完整的类型。如果结果指针未正确对齐(57)   指向类型,行为未定。

在这种情况下,的结果指针保证正确对齐(因为结构的第一个成员必须与结构重合),因此此限制不适用于此处。适用的是对指针使用的额外限制,要求只能通过与对象的“有效类型”兼容的指针访问对象...在这种情况下,x的有效类型为{{1}因此无法通过结构指针访问它。

注意,与某些声明相反,指针类型之间的转换不限于往返使用。该标准表示可以转换指针,但附带条件是何时此类转换会导致未定义的行为。在其他地方,它给出了使用结果类型的指针的语义。标准中的往返保证附加的规范...如果没有明确说明,您可以依赖的事项:

  

否则,当再次转换回来时,结果将与原始指针进行比较。

这指定了往返旅行的保证,不是往返旅行的限制。

但是,如上所述,“有效类型”语言 是对转换产生的指针使用的限制。