转换回早期类型

Casting back to earlier type

本文关键字:类型 转换      更新时间:2023-10-16

我看到在某些代码中,我们有一个有符号值被传递给函数(它需要无符号值)。返回的值再次转换回有符号(目的是获取原始有符号值)。即使它目前有效..我怀疑,在所有可能的情况下,在这种情况下会保留原始签名值吗?

有人告诉我选角不好,应该尽可能避免。一些计算机科学专家可以告诉我,什么时候可以破解这段代码吗?

我知道的所有实现(在不同平台上与C++合作 20+ 年后),通过什么都不做来转换有符号<->无符号整数,即二进制值保持不变。

这种行为的根源在于处理(即不处理)C 中的整数溢出。拥有这样的遗产,这种签名/未签名的转换是一件合理的事情。我认为这永远不会改变。

总而言之,有符号->无符号->有符号和无符号->有符号->无符号的转换是100%安全的。当您有偶数次转换时,您需要注意数字的值是未使用最高位的非负整数,在这种情况下,类型是有符号还是无符号并不重要。否则,应使用自己的代码显式处理超出范围的值。

标准的整数提升部分很好地涵盖了有符号和无符号的转换。您的问题标记为C和C++,尽管我可以掸去这两个标准的灰尘,但我会在这里向您展示C99标准的相关部分。

关于将大小相似的有符号整数提升为无符号整数,其中有符号整数不在无符号整数的范围内(即它小于零(0)):

C99 6.3.1.3-p2

否则,如果新类型

是无符号的,则通过重复添加或减去比新类型中可以表示的最大值多一个来转换值,直到该值在新类型的范围内。

这本质上意味着"添加(UINT_MAX+1)"。

例如,在典型的 32 位系统上,UINT_MAX 0xFFFFFFFF 。现在假设您要转换以下内容:

int ival = -1;
unsigned int uval = (unsigned int)ival;

根据该标准,ival将通过执行以下操作提升为unsigned int

uval = (UINT_MAX+1) + (-1);

当然,这会导致uval = UINT_MAX;.这是由标准定义的。

从未签名转换为签名是另一回事。紧跟在上述标准上一节之后的是:

C99 6.3.1.3-p3

否则,新类型是有符号的,并且无法在其中表示值;结果要么是实现定义的,要么是实现定义的信号。

这实质上意味着,如果无符号中的不在有符号整数的允许范围内,则不能依赖无符号转换为相同大小的有符号结果来具有实现不可执行的行为。换句话说:

unsigned int uval = INT_MAX;
int val = (int)uval; 

定义了行为,因为INT_MAX是有符号整数范围内的值,而这:

unsigned int uval = INT_MAX + n;
int val = (int)uval; 

对于一些任意n使得 (INT_MAX+n <= UINT_MAX) 是实现定义的。因此,不要依赖它。标准中应该吓唬你避免这样做的特定部分是潜在的:"一个实现定义的信号被提出"。伊克斯。只是不要这样做。


上面文本中的示例

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main(int argc, char *argv[])
{
    int ival = -1;
    unsigned int uval = (unsigned int)ival;
    printf("%d : %un", ival, uval);
    uval = INT_MAX;
    ival = (int)uval;
    printf("%d : %un", ival, uval);
    uval += 1;
    ival = (int)uval;
    printf("%d : %un", ival, uval);
    return 0;
}

输出平台:Apple LLVM 版本 4.2 (clang-425.0.28)

-1 : 4294967295
2147483647 : 2147483647
-2147483648 : 2147483648

从有符号类型到无符号类型的转换定义良好;结果是原始值模 2^n,其中 n 是无符号类型的值表示中的位数。也就是说,负值被包装为大的正值。但是,从无符号类型转换为有符号类型时,如果正在转换的值可以在目标类型中表示,则该值保持不变;如果无法表示,则结果是定义的实现。因此,将较大的无符号值转换为不足以容纳它的有符号值不需要生成负值。