【问题标题】:Does copying data byte wise between types break strict aliasing?在类型之间按字节复制数据会破坏严格的别名吗?
【发布时间】:2017-07-19 09:23:29
【问题描述】:

假设我有两种大小相同的AB,并且我有两个变量

A a = ... ; // Initialized to some constant of type A
B b;

如果我将 a 的内容复制到 b 使用类似 -

assert(sizeof(A) == sizeof(B));
size_t t;
for( t=0; t < sizeof(A); t++){
    ((char*)&b)[t] = ((char*)&a)[t];
}

这是否违反了 C 的严格别名规则? 我知道将指针指向 char* 并读取它不是 UB,但我担心分配中涉及的两种取消引用。

如果这不是 UB,这可以成为类型双关语的有效方式吗?

【问题讨论】:

  • 这不是由于严格的别名造成的 UB(实际上,您在这里没有别名,除了 char*,这是允许的)。这是 UB,因为您可能刚刚用陷阱表示填充 B。您正式复制的位模式表示的值使b 处于不确定状态,IIRC。
  • @StoryTeller 如果保证B 是一个没有陷阱表示的类型,这是“OK”吗?
  • Well, yeah。但该标准并没有向您承诺任何有意义的语义。
  • @StoryTeller,如果AB 是具有完全相同字段的两种结构类型,我是否可以期望行为与单独复制每个字段相同。因为该标准保证,如果两个结构使用相同的成员定义,则任何实现都必须具有相同的内存布局。
  • 标准确实保证了这一点。我想你应该没事。任何被复制的填充位的内容都不会干扰,因为它们只是未指定的。所有相应的成员正确复制他们的值。作为旁注,我认为您的循环应该替换为对memcpy 的调用。它需要更少的投射(反过来看起来更干净)。

标签: c language-lawyer undefined-behavior


【解决方案1】:

此代码不违反别名规则。来自最新草案 (n1570),§6.5 第 7 节:

一个对象的存储值只能由具有以下之一的左值表达式访问 以下类型:
— 与对象的有效类型兼容的类型,
— 与对象的有效类型兼容的类型的限定版本,
— 对应于有效类型的有符号或无符号类型 对象,
— 一种类型,它是有符号或无符号类型,对应于 对象的有效类型,
— 聚合或联合类型,其中包括上述类型之一 成员(递归地包括子聚合或包含联合的成员),或
一种字符类型

(强调我的)

我很担心这项任务中涉及的两种反引用。

这些取消引用正在 使用字符类型访问存储的值

当然,如果您的A 的表示不是B 的有效表示,您仍然可以触发未定义的行为

【讨论】:

  • 我认为这个答案毫无疑问地消除了混乱。从上面在 cmets 中的讨论(以及您的最后一行)很明显,如果 B 没有陷阱表示,它就不会是 UB。但是该标准不保证任何关于语义的事情。但是,如果 A 和 B 是具有完全相同字段的两个结构类型,我是否可以期望行为与单独复制每个字段相同。因为该标准保证,如果两个结构使用相同的成员定义,则任何实现都必须具有相同的内存布局。
  • 在目的地只能通过指针知道的情况下,6.5p6 会在工作中抛出一个扳手。
【解决方案2】:

如果目标具有声明的类型,则没有问题,但在仅通过指针知道目标的情况下,标准是模棱两可的。根据绝对可怕的 6.5p6,使用memcpymemmove 或“作为字符类型的数组”[无论这意味着什么] 复制数据将导致源的有效类型应用于目标。该标准没有指定在不将操作视为复制“字符类型数组”的情况下复制字节序列必须做什么。

【讨论】:

  • 在我的问题中,目标类型是完整的,因为我强加了 A 和 B 的大小相等的条件。但感谢您的洞察力。很少看到标准对某事如此模糊(根据我的经验)。
  • @AjayBrahmakshatriya:标准的作者认识到不同的应用领域需要以不同的方式使用别名,因此认为适合任何特定领域的任何高质量编译器都应该在适合的情况下支持别名该字段标准是否要求,因此无需深入研究每个可能的极端情况。尽管如此,6.5p6 确实很糟糕,因为它为真正的优化提供了有用的机会,但也为恶作剧提供了重大机会。
  • 我不认为这是正确的:声明类型有效类型都不可能只用于分配的对象 i> ...一旦他们有了一个有效的类型,它就会一直存在。如果你愿意,我明天可以试着在标准中找到相关的段落。
  • 举例说明:如果你有B *b = malloc(sizeof(B));,然后通过char *复制a,你确实会遇到问题,因为*b会得到有效类型 char。但是使用B b;,不会有问题。
  • @FelixPalmen:在目的地是简单声明的对象的特定场景中,没有问题。在目的地仅由指针知道的情况下[这种情况适用于分配的对象,但也适用于函数从其他地方接收指针的情况],在使用字节复制作为类型的情况下,轮子会脱落转换。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-09
  • 2022-10-15
  • 2013-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多