【问题标题】:Strict aliasing rules and std::copy严格的别名规则和 std::copy
【发布时间】:2011-09-13 20:36:28
【问题描述】:

我有一个包含“序列化”数据的字符数组,我需要将其解释为“int”。以前我只是将指向该位置的指针转换为 'int*' 并取消引用以获取 int 数据,但尽管它对我来说效果很好,但它违反了严格的别名规则,因此违反了未定义的行为。

所以现在我使用 memcpy 将字节复制到一个 int 中,我相信这不是未定义的行为。但是我可以使用“std::copy”吗?

例如

char data[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int i;

std::copy(data, data+sizeof(int), reinterpret_cast<char*>(&i));

这本身并没有违反严格的别名规则,但任何可能的实现都会这样做......但是 memcpy 有同样的问题,这是“允许的”。

这是标准编译代码还是我需要坚持使用 memcpy?

编辑:我应该补充一点,我很喜欢关于如何最好地做到这一点的答案,它们很有趣,但我的问题更多的是关于这是否合法而不是如何我这样做。

【问题讨论】:

  • memcpy 不保证在重叠范围内正常工作,您需要为此使用memmove
  • 我假设 OP 执行memcpy(&amp;i, data, sizeof(int)),这不涉及重叠范围。
  • 糟糕——我的错。混叠混叠。 叹息
  • 我会使用memcpy,因为它表示低级别操作,可以让你免于丑陋的演员阵容。
  • @BoP 可以说,reinterpret_cast 比(无强制转换的)memcpy 更有效地向低级操作发出信号。

标签: c++


【解决方案1】:

这等价于std::memcpy(&amp;i, data, sizeof(int)),并遇到依赖于字节顺序和sizeof(int) &lt;= sizeof(data) 的假设的相同问题,这些问题与平台相关。 char * 不受严格别名规则的约束。

【讨论】:

  • char* 只有一种方式...您可以读取另一种类型的字节,但不能将 char* 转换为另一种类型然后访问它。 (是的,我意识到这有字节序问题等,但它们对我的实际应用程序来说不是问题)
  • @JohnB: 没错,但是memcpycopy 都不需要将它们的char* 参数转换为任何其他类型才能起作用。他们会进行逐字节的复制,至少在概念上是这样(优化编译器可能会将其转换为更快的副本)。
【解决方案2】:

为什么不直接写:

#if __YOU_DEFINE_IF_LITTLE_ENDIAN__
#define TO_INT(d) ((((int)(d)[0]))|
                  (((int)(d)[1])<<8)|
                  (((int)(d)[2])<<16)|
                  (((int)(d)[3])<<24))
#else
#define TO_INT(d) ((((int)(d)[3]))|
                  (((int)(d)[2])<<8)|
                  (((int)(d)[1])<<16)|
                  (((int)(d)[0])<<24))
#endif

然后,例如,要获取代码中 {4, 5, 6, 7} 部分的 int,请编写:

i = TO_INT(data+4);

注意:这不是复制粘贴的代码,它提供了一个想法。如果您有不同的系统和不同大小的int,或者可能给宏提供char * 之外的其他类型的指针,请自行检查错误。

【讨论】:

  • 你为什么要这样做?这太糟糕了。如果有的话,使用函数而不是宏,并编写可读的代码。
  • 部分是因为我的代码就是一个例子。我也想对浮点数和 64 位整数做同样的事情,但对它们来说不太可行。
  • 宏/功能/随便什么,这不是重点。我的意思是,您可以根据平台的字节序使用&lt;&lt; 将数据转换为int,而不是使用memcpy(在我看来,这实际上很可怕)。此外,代码简单,规则重复组件。如果给定奇怪的参数,只需一些额外的括号以确保它不会crack。它相当容易阅读。
  • 如果你不喜欢括号,你也可以写:((int)(d)[0]|(int)(d)[1]&lt;&lt;8|(int)(d)[2]&lt;&lt;16|(int)(d)[3]&lt;&lt;24) 这会很好,但我自己更喜欢更重的括号,实际上是为了可读性!这样任何阅读它的人都不需要参考优先表
  • @JohnB 是的,对于会成为问题的浮点数。
【解决方案3】:

这是否满足您的需求?

int f (const char* x, size_t index)
  {
  const int* p = (const int*)x ;
  return p[index] ;
  }

gcc 4.5.2 在没有任何警告的情况下编译它,即使打开了-O3 -Wall

【讨论】:

  • 当您通过指向不同类型的指针访问定义为 const char* 的内存时,这不是未定义的行为吗?
  • 严格别名允许通过假设指向不同类型的指针不指向(或重叠)相同地址来进行积极优化。如果在return 语句之前有一个语句x[99]=101;(暂时忘记const 限定符),则会违反严格的别名,并且编译器可能会返回p[index] 的原始值或新值。但这不可能在这里发生,因为x 不会改变。
猜你喜欢
  • 2018-12-14
  • 2015-10-15
  • 2017-02-25
  • 1970-01-01
  • 2014-07-13
  • 2015-03-30
  • 1970-01-01
  • 2019-01-29
相关资源
最近更新 更多