将 int32 转换为 uint32 是空操作吗?

Is casting int32 to uint32 a no-op?

我想将 int32_t 的位粘贴到类型 uint32_t 中而不进行任何转换,只是重新解释。下面的代码正是我想要的:

int32_t  iA = -1;
uint32_t uA = *(uint32_t*)&iA;

但我想知道,我是否可以依靠以下更容易编写的 cast 生成相同(或更少)的程序集,最好只是 movs? (即,它永远不会对它做 "math",保持底层位不变。)

int32_t  iB = -1;
uint32_t uB = (uint32_t)iB;

assert(uA == uB); // ?

使用 memcpy 是避免在别名类型时出现未定义行为的常见解决方案。 在评论中指出,仅在符号上有所不同的别名类型是可以的,但是 floatint 就不是这种情况了。

memcpy 只要对象表示对该类型有效就有效。

编译器非常擅长优化 memcpy 调用,在本例中为 the call is completely optimized away

在 C++20 之前,有符号整数的表示是实现定义的。但是,即使在 C++20 之前,std::intX_t 也是 guaranteed 具有 2s'-补码表示:

int8_t, int16_t, int32_t, int64_t - signed integer type with width of exactly 8, 16, 32 and 64 bits respectively with no padding bits and using 2's complement for negative values (provided only if the implementation directly supports the type)

写的时候

std::int32_t  iA = -1;
std::uint32_t uA = *(std::uint32_t*)&iA;

您将获得设置了所有位的值。如果 "type is similar to ... a type that is the signed or unsigned type corresponding to the dynamic type of the object",则允许​​通过 std::uint32_t* 类型的指针访问 std::int32_t 的标准 says。因此,严格来说,在解引用指针之前,我们必须确保 std::uint32_t 确实是对应于 std::int32_t 的无符号类型:

static_assert(std::is_same_v<std::make_unsigned_t<std::int32_t>, std::uint32_t>);

写的时候

std::int32_t  iB = -1;
std::uint32_t uB = (std::uint32_t)iB;

您依赖于转换为 well-defined 的无符号类型,并且保证产生相同的值。

至于程序集,两个转换都是空操作:

std::uint32_t foo() {
    std::int32_t  iA = -1;
    static_assert(std::is_same_v<std::make_unsigned_t<std::int32_t>, std::uint32_t>);
    return *(std::uint32_t*)&iA;
}

std::uint32_t bar() {
    std::int32_t  iB = -1;
    return (std::uint32_t)iB;
}

result in:

foo():
        mov     eax, -1
        ret
bar():
        mov     eax, -1
        ret