【发布时间】:2017-05-07 17:00:40
【问题描述】:
在 C++ 入门中它说“如果我们为无符号类型的对象分配一个超出范围的值,则结果是该值的余数模目标类型可以容纳的值的数量。”
举个例子:
int main(){
unsigned char i = -1;
// As per the book the value of i is 255 .
}
任何人都可以向我解释一下这是如何工作的。
【问题讨论】:
在 C++ 入门中它说“如果我们为无符号类型的对象分配一个超出范围的值,则结果是该值的余数模目标类型可以容纳的值的数量。”
举个例子:
int main(){
unsigned char i = -1;
// As per the book the value of i is 255 .
}
任何人都可以向我解释一下这是如何工作的。
【问题讨论】:
结果是取模目标类型可以容纳的值的数量的余数
从“目标类型可以容纳的值的数量”开始。对于unsigned char,这是什么?范围是从 0 到 255,包括 0 到 255,因此总共有 256 个值可以表示(或“保持”)。
一般来说,可以用特定的无符号整数表示的值的数量由 2n 给出,其中 n 是用于存储的位数那种类型。
unsigned char 是 8 位类型,所以 28 == 256,正如我们已经知道的那样。
现在,我们需要执行 模 运算。在您将 -1 分配给 unsigned char 的情况下,您将拥有 -1 MOD 256 == 255。
一般来说,公式是:x MOD 2n,其中 x 是您尝试分配的值,n 是您尝试分配的类型的位宽。
更正式地说,这是在 C++11 语言标准(第 3.9.1/4 节)中规定的。它说:
声明为无符号的无符号整数应遵守算术模 2n 的定律,其中 n 是该特定整数大小的值表示中的位数。 *
* 这意味着无符号算术不会溢出,因为无法由生成的无符号整数类型表示的结果以比可以表示的最大值大一的数字为模减少得到的无符号整数类型。
也许考虑模运算(以及您最常看到使用的描述)的更简单方法是上溢和下溢环绕。您从 -1 开始,它下溢了 unsigned char 的范围(即 0–255),因此它环绕到最大可表示值(即 255)。
【讨论】:
int 类型,小于int 的类型在用于表达式时提升为int。因此,表达式-1 % 256 将给出一个int 的结果。 -1 可以表示为int,所以它就是答案。这与您的问题完全不同,后者将 -1 的值存储在 unsigned char 类型中。
% 运算符不完全是模数 - 它是 remainder,并且语义略有不同。如果第一个参数为负数,则返回负数。您需要逻辑算术模运算。
它在 C 中等同于 C++,但措辞不同:
6.3.1.3 有符号和无符号整数
1当一个整数类型的值被转换为除_Bool以外的其他整数类型时,如果该值可以用新的类型表示,则保持不变。
2否则,如果新类型是无符号的,则在新类型所能表示的最大值的基础上反复加减一,直到值在新类型的范围内。
p>3 否则,新类型是有符号的,值不能在其中表示;结果要么是实现定义的,要么引发实现定义的信号。
【讨论】:
文字1 是int 类型。对于这个解释,我们假设sizeof(int) == 4 很可能是这样。那么二进制中的1 看起来像这样:
00000000 00000000 00000000 00000001
现在让我们应用一元减号运算符来获得-1。我们假设 two 的补码 被使用,因为它很可能是(查找二的补码以获得更多解释)。我们得到:
11111111 11111111 11111111 11111111
请注意,在上面的数字中,第一位是符号位。
当您尝试将此数字分配给 unsigned char(其中包含 sizeof(unsigned char) == 1)时,该值将被截断为:
11111111
现在如果你把它转换成十进制,你会得到255。这里第一位不被视为符号位,因为类型是unsigned。
【讨论】:
11111111 11111111 11111111 11111111 以外的位模式会更清楚11111111 的来源。
11111111。 -1 在一个补码中的表示是11111110。将值 -1 转换为无符号整数的结果将始终给出最大可能的结果,其中所有位都已设置:11111111。使用你的方法会根据整数表示给出不同的结果,显然不是这样。
用 Stroustrup 的话来说:
如果目标类型是
unsigned,则结果值只是来自源的位数与目标中的位数相同(必要时丢弃高位)。更准确地说,结果是与源整数模 2 到第 n 次一致的最小无符号整数,其中 n 是用于表示无符号类型的位数。
摘自 C++ 标准 N3936:
对于每个标准的有符号整数类型,都存在一个对应的(但不同的)标准无符号 整数类型:“unsigned char”、“unsigned short int”、“unsigned int”、“unsigned long int”、 和“unsigned long long int”,每个都占用相同的存储量并且具有相同的 对齐要求(3.11)作为相应的有符号整数类型47;也就是说,每个有符号整数类型 具有与其对应的无符号整数类型相同的对象表示。
【讨论】:
我自己正在阅读 C++ 入门的摘录,我认为我已经找到了一种数学方法来计算这些值是如何产生的(如果我错了,请随时纠正我 :))。以下面的特定代码为例。
unsigned char c = -4489;
std::cout << +c << std::endl; // will yield 119 as its output
那么这个119的答案是怎么出来的呢?
取 4489 除以字符总数,即 2^8 = 256,余数为 137。
4489 % 256 = 137。
现在只需从 256 中减去 137。
256 - 137 = 119。
这就是我们简单地推导出 mod 值的方式。也请自己尝试其他价值观。对我来说非常准确!
【讨论】: