我在一些代码中看到,我们将一个带符号的值传递给函数(它采用无符号值)。返回的值再次转换回签名(目的是获取原始签名值)..即使它当前有效..我怀疑,在这种情况下,在所有可能的情况下,是否会保留原始签名值?
我被告知施法很糟糕,应该尽可能避免..有些计算机科学专家可以告诉我这个代码何时可以被打破?
答案 0 :(得分:4)
我所知道的所有实现(在不同平台上使用C ++ 20多年后),转换为signed< - >无效的无符号整数,即二进制值不变。
这种行为的根源在于处理(即不处理)C中的整数溢出。具有这样的遗产,这种签名/无符号转换是合理的做法。我认为这不会改变。
总结一下,转换签名 - > unsigned - >签名和未签名 - >签名 - > unsigned是100%安全的。当您进行偶数次转换时,您需要注意数字的值是非负整数,而不使用最高位,在这种情况下,如果类型是有符号或无符号的,则不重要。否则,您应该使用自己的代码明确地处理超出范围的值。
答案 1 :(得分:3)
标准的整数促销部分非常适合签名和未签名的转换。你的问题标记为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不在signed int的允许范围内。换句话说:
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 : %u\n", ival, uval);
uval = INT_MAX;
ival = (int)uval;
printf("%d : %u\n", ival, uval);
uval += 1;
ival = (int)uval;
printf("%d : %u\n", ival, uval);
return 0;
}
输出平台:Apple LLVM 4.2版(clang-425.0.28)
-1 : 4294967295
2147483647 : 2147483647
-2147483648 : 2147483648
答案 2 :(得分:1)
已定义从有符号类型到无符号类型的转换;结果是原始模2 ^ n,其中n是无符号类型的值表示中的位数。也就是说,负值被包装成大的正值。但是,当您从unsigned转换为signed类型时,如果转换的值可以在目标类型中表示,则值不会更改;如果无法表示,则结果为实现定义。因此,将大的无符号值转换为不足以容纳它的有符号值,不需要产生负值。