如果我为无符号变量分配负值会发生啥?

Posted

技术标签:

【中文标题】如果我为无符号变量分配负值会发生啥?【英文标题】:What happens if I assign a negative value to an unsigned variable?如果我为无符号变量分配负值会发生什么? 【发布时间】:2011-02-12 06:46:10 【问题描述】:

我很想知道如果我将一个负值赋给一个无符号变量会发生什么。

代码看起来有点像这样。

unsigned int nVal = 0;
nVal = -5;

它没有给我任何编译器错误。当我运行程序时,nVal 被分配了一个奇怪的值!会不会是某个 2 的补码值被分配给了nVal

【问题讨论】:

我的预感(尚未在标准中找到它)是该行为在技术上是未定义的。此外,我怀疑您会在几乎可以找到的任何编译器上看到您所期望的。因此,虽然您通常会看到这种行为,但依靠它可能不是一个好主意。 它不是未定义的(参见 §4.7/2),但表示(例如 2s 补码)不是标准规定的。 @gf(以下等),很酷。看起来行为实际上已明确定义为您所期望的,@viswanathan。 第二行相当于nVal = (unsigned int) -5;-5unsigned int 的转换在 6.3.1.3 中定义。 2s 补码中的表示不是标准规定的,但转换为无符号的算法是:“通过重复加或减一个比新类型中可以表示的最大值多一的方式转换值,直到该值在范围内新类型的。” @Pascal:你好像指的是 C99,但问题被标记为 C++。 【参考方案1】:

当您为无符号变量分配负值时,它会使用 2 的补码方法对其进行处理,并在此方法中将所有 0 翻转为 1,将所有 1 翻转为 0,然后将其加 1。在您的情况下,您正在处理 4 字节(32 位)的 int,因此它尝试对 32 位数字使用 2 的补码方法,这会导致较高位翻转。例如:

┌─[student@pc]─[~]
└──╼ $pcalc 0y00000000000000000000000000000101      # 5 in binary
        5                       0x5                     0y101
┌─[student@pc]─[~]
└──╼ $pcalc 0y11111111111111111111111111111010      # flip all bits  
      4294967290      0xfffffffa      0y11111111111111111111111111111010
┌─[student@pc]─[~]
└──╼ $pcalc 0y11111111111111111111111111111010 + 1  # add 1 to that flipped binarry
      4294967291      0xfffffffb      0y11111111111111111111111111111011

【讨论】:

【参考方案2】:

对于官方答案 - 第 4.7 节 conv.integral

"如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模 2n 其中n 是用于表示无符号类型的位数). [注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。—尾注]

这实质上意味着,如果底层架构存储在非二进制补码的方法中(如有符号幅度或二进制补码),则转换为无符号的行为必须与二进制补码一样。

【讨论】:

与源整数一致的最小无符号整数是什么意思? @DavidRodríguez-dribeas 例如,5 和 3 是“全等模 2”,因为 5%2 和 3%2 都是 1。 它与哪个版本的 C++ 标准相关?全部? 所以 (uint)((int)a + (int)b) 可能是未定义的,因为 a 和 b 可能溢出,但是 (uint)a + (uint)b 是明确定义的因为 uint 允许溢出。对于所有定义明确的总和,两者都会给出相同的结果?【参考方案3】:

是的,你是对的。分配的实际值类似于除第三位之外的所有位。 -1 是所有位设置(十六进制:0xFFFFFFFF),-2 是除第一个以外的所有位,依此类推。您看到的可能是十六进制值 0xFFFFFFFB,十进制对应 4294967291。

【讨论】:

位与此无关,未指定整数表示。 你的回答是正确的、严格的、切中要害的,而且我永远不会在课堂上使用。 查看我对 -5 的 2 补码的回答。我认为您在这里的二进制值上没有正确计算。【参考方案4】:

它将代表-5(2的补码)的位模式分配给无符号整数。这将是一个很大的无符号值。对于 32 位整数,这将是 2^32 - 5 或 4294967291

【讨论】:

比特与此无关。 @BenVoigt:很公平,我的意思是它与位的解释方式无关。 (也就是说,引用部分中的“位”只是ceil(log_2(x)) 的简写。) @GManNickG 位的(如,属于位)? 2 的赞美(that's very nice of you)?啊啊啊啊啊! @NullUserException:哈哈,我知道。写“*'s”代替“*s”是我有一段时间的一个糟糕的习惯。至于恭维而不是补充,那只是纯粹的愚蠢。 :) 简单是关键。这个答案有。 (2^32 - 5) 比引用文档更好地解释了这种行为。【参考方案5】:

你是对的,有符号整数以2的补码形式存储,无符号整数存储在unsigned binary representation中。 C(和 C++)不区分这两者,所以你最终得到的值只是 2 的补码二进制表示的无符号二进制值。

【讨论】:

2的恭维中可能不存储。 如果某物“存储在 2 中”是什么意思? @GManNickG @JeremyF:不是“2”,“2 的恭维”。这是一个 Google 术语,也是一种表示有符号整数的方式。【参考方案6】:

它将显示为最大无符号整数值的正整数 - 4(值取决于计算机体系结构和编译器)。

顺便说一句 您可以通过编写一个简单的 C++“hello world”类型程序来检查这一点并亲自查看

【讨论】:

我写并检查了它,这就是我问这个问题的原因,但我不知道编译器是如何得出这个正值的。谢谢 不幸的是,对于 C++,编写程序来测试行为并不总是一个好主意。例如,如果试图测试在 signed 溢出的情况下会发生什么,它将导致未定义的行为,这不能保证在每台机器/编译器上都是相同的。

以上是关于如果我为无符号变量分配负值会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何在python中将有符号整数转换为无符号整数

C++ 使用 istringstream 将整数读取为无符号字符

有没有一种方法可以在不使用大量分支语句的情况下为无符号整数编写 qsort 比较函数?

c语言:输出枚举型变量的值,应该用啥格式说明

如何解决 MS SQL 中不受支持的无符号整数字段类型?

在 Swift 中将有符号转换为无符号