我是否正确地说有符号和无符号整数之间的区别是:
还有其他差异吗?
无符号可以保持较大的正值,而不是负值。
是。
无符号使用前导位作为值的一部分,而带符号版本使用最左侧位来标识数字是正数还是负数。
表示有符号整数的方法有很多种。最容易想象的是使用最左边的位作为标志(sign and magnitude),但更常见的是two's complement。两者都在大多数现代微处理器中使用 - 浮点使用符号和幅度,而整数运算使用二进制补码。
有符号整数可以包含正数和负数。
是
(回答第二个问题)通过仅使用符号位(而不是2的补码),最终可以得到-0。不是很漂亮
无符号整数更有可能在特定陷阱中捕获您而不是有符号整数。陷阱来自这样的事实:虽然上面的1和3是正确的,但是两种类型的整数都可以被分配一个超出它可以“保持”范围的值,并且它将被静默转换。
unsigned int ui = -1;
signed int si = -1;
if (ui < 0) {
printf("unsigned < 0\n");
}
if (si < 0) {
printf("signed < 0\n");
}
if (ui == si) {
printf("%d == %d\n", ui, si);
printf("%ud == %ud\n", ui, si);
}
当你运行它时,即使两个值都分配给-1并且声明不同,你也会得到以下输出。
signed < 0
-1 == -1
4294967295d == 4294967295d
C中的有符号整数表示数字。如果a
和b
是有符号整数类型的变量,则标准将永远不要求编译器使表达式a+=b
存储到a
中除了它们各自值的算术和以外的任何值。可以肯定的是,如果算术总和不适合a
,处理器可能无法将其放在那里,但标准不要求编译器截断或包装值,或者做任何其他事情,如果值超过其类型的限制。请注意,虽然标准不要求它,但允许C实现使用有符号值捕获算术溢出。
C中的无符号整数表现为整数的抽象代数环,除了涉及对较大类型的转换或操作的场景外,它们是2的幂的一致模。将任意大小的整数转换为32位无符号类型将产生对应于与整数mod 4,294,967,296一致的事物的成员。从2中减去3得到4,294,967,295的原因是,将一些与3一致的东西添加到与4,294,967,295一致的东西将产生与2一致的东西。
抽象代数环类型通常是方便的东西;不幸的是,C使用签名作为一个类型是否应该表现为环的决定因素。更糟糕的是,无符号值在转换为较大类型时被视为数字而不是环成员,而小于int
的无符号值在对它们执行任何算术时将转换为数字。如果v
是uint32_t
等于4,294,967,294
,那么v*=v;
应该制作v=4
。不幸的是,如果int
是64位,那么就不知道v*=v;
可以做什么。
鉴于标准本身,我建议在需要与代数环相关联的行为的情况下使用无符号类型,并在想要表示数字时使用签名类型。不幸的是,C以它的方式绘制了区别,但它们就是它们的本质。
C中有符号值和无符号值之间唯一保证的差别是有符号值可以是负数,0或正数,而无符号值只能是0或正数。问题是C没有定义类型的格式(所以你不知道你的整数是两个补码)。严格来说,你提到的前两点是不正确的。
在嵌入式系统上编程时,必须使用无符号整数。在循环中,当不需要有符号整数时,使用无符号整数将节省设计此类系统所必需的安全性。
我将在x86上的硬件级别上进行分析。除非您正在编写编译器或使用汇编语言,否则这几乎无关紧要。但很高兴知道。
首先,x86本身支持有符号数字的two's complement表示。您可以使用其他表示形式,但这需要更多指令,通常会浪费处理器时间。
“原生支持”是什么意思?基本上我的意思是你有一组用于无符号数字的指令和另一套用于有符号数字的指令。无符号数字可以与有符号数字位于相同的寄存器中,实际上,您可以混合有符号和无符号指令,而无需担心处理器。由编译器(或汇编程序员)来跟踪数字是否已签名,并使用适当的指令。
首先,二进制补码数具有加法和减法与无符号数相同的特性。数字是正数还是负数没有区别。 (所以你只需要继续前进,ADD
和SUB
你的数字而不用担心。)
在进行比较时,差异开始显现。 x86有一种区分它们的简单方法:上/下表示无符号比较,大于/小于表示已签名比较。 (例如,JAE
的意思是“如果超过或等于跳跃”并且未签名。)
还有两组乘法和除法指令来处理有符号和无符号整数。
最后:如果你想检查溢出,你会对签名和无符号数做不同的处理。
他只询问签名和未签名。不知道为什么人们会在这里添加额外的东西。让我告诉你答案。
这个解释是关于8位数字系统。
完整性只需几点:
根据我们在课堂上学到的东西,有符号整数可以表示正数和负数,而无符号整数只是非负数。
例如,查看一个8位数字:
无符号值0
到255
签名值范围从-128
到127
除第2点以外的所有内容都是正确的。签名整数有许多不同的符号,一些实现使用第一个,其他实现使用最后一个,而其他实现使用完全不同的东西。这一切都取决于您正在使用的平台。
另一个区别是当您在不同大小的整数之间进行转换时。
例如,如果要从字节流中提取整数(为简单起见,则为16位),使用无符号值,您可以执行以下操作:
i = ((int) b[j]) << 8 | b[j+1]
(应该可能会抛出第二个字节,但我猜测编译器会做正确的事情)
使用签名值,您将不得不担心签名扩展并执行:
i = (((int) b[i]) & 0xFF) << 8 | ((int) b[i+1]) & 0xFF
一般来说这是正确的。在不知道为什么要寻找差异的情况下,我无法想到签名和未签名之间的任何其他区别。
除了其他人所说的,在C中,你不能溢出无符号整数;行为被定义为模数运算。你可以溢出有符号整数,理论上(虽然在当前的主流系统中没有实际),溢出可能会触发故障(可能类似于除零故障)。