将uint32_t转换为int32_t,不会有溢出或过度复杂的风险

时间:2016-04-18 21:16:37

标签: c++ undefined-behavior

如何使用C ++快速将uint32_t转换为int32_t

有些尝试:

uint32_t y = UINT32_MAX;
int32_t x = (int32_t)y; // UB on overflow
int32_t x = *(int32_t*)&y; // does this violate strict aliasing?

3 个答案:

答案 0 :(得分:5)

union { 
  int32_t i; 
  uint32_t u;
} u;
u.i = ...;
printf("%" PRIu32 "\n", u.u);

这和memcpy(&uint_var, &int_var, sizeof uint_var)是在不调用未定义行为的情况下进行此类转换的两种标准方法。

另见:

答案 1 :(得分:5)

int32_t x = (int32_t)y;不是溢出而不是UB。 溢出是算术运算产生超出可表示值范围的结果的时间。但是,转换不是算术运算。

这种情况是实现定义的行为。我所知道的所有实现都将行为定义为不对表示进行任何更改。

请注意,此处不需要演员表。你可以写int32_t x = y;。 实际上,这更简单,并将始终有效。如此多的代码依赖于此,没有供应商会定义任何其他行为(并不是他们有任何理由这样做)。

int32_t x = *(int32_t*)&y不是UB。它不违反严格别名,因为允许类型的签名版本为无符号版本设置别名。保证此代码生成int32_t,其表示形式与对应的uint32_t相同(即"包装",因为这些类型保证是2'补码)。

答案 2 :(得分:1)

#include <assert.h>
#include <limits.h>
#include <stdlib.h>

int makesigned(unsigned x) {
  if (x <= (unsigned) INT_MAX) {
    return (int) x;
  }

  /* assume 2's complement */
  if (x >= (unsigned) INT_MIN) {
    return 0 - (int)(-x);
  }

  abort();
  return 0;
}

int main(void) {
  assert(makesigned(0) == 0);
  assert(makesigned(INT_MAX) == INT_MAX);
  assert(makesigned(UINT_MAX) == -1);
  assert(makesigned(INT_MIN) == INT_MIN);
  return 0;
}