在C或C中,据说size_t(无符号整数数据类型)可以容纳的最大数量与向该数据类型强制转换-1相同 . 例如见Invalid Value for size_t
为什么?
我的意思是,(谈论32位整数)AFAIK最重要的位保持有符号数据类型的符号(即,位0x80000000形成负数) . 那么,1是0x00000001 .. 0x7FFFFFFFF是int数据类型可以容纳的最大正数 .
然后,AFAIK的-1 int的二进制表示应该是0x80000001(也许我错了) . 将int转换为unsigned时,为什么/如何将此二进制值转换为完全不同的任何值(0xFFFFFFFF)?或..如何形成0xFFFFFFFF的二进制-1?
我毫不怀疑在C :((unsigned int)-1)== 0xFFFFFFFF或((int)0xFFFFFFFF)== -1同样真实而不是1 1 == 2,我只是想知道为什么 .
6 回答
那是two's complement编码 .
主要的好处是,无论使用的是unsigned还是signed int,都可以获得相同的编码 . 如果从0减去1,则整数只是环绕 . 因此,小于0的1是0xFFFFFFFF .
它被称为二补 . 要得到一个负数,反转所有位然后加1 . 所以要将1转换为-1,将其反转为0xFFFFFFFE,然后加1以使其为0xFFFFFFFF .
至于为什么这样做,Wikipedia说:
关于为什么
(unsigned)-1
给出最大可能的无符号值的第一个问题只是偶然与两个补码相关 . -1转换为无符号类型的原因给出了该类型可能的最大值,因为标准表示无符号类型“遵循算术模2n的定律,其中n是特定整数大小的值表示中的位数“ .现在,对于2的补码,最大可能的无符号值和-1的表示恰好相同 - 但即使硬件使用另一种表示(例如1的补码或符号/幅度),将-1转换为无符号类型必须仍然产生该类型的最大可能值 .
因为int -1的位模式是十六进制无符号的FFFFFFFF . 11111111111111111111111111111111二进制无符号 . 但在int中,第一位表示它是否为负数 . 但是在unsigned int中,第一位只是额外的数字,因为unsigned int不能为负数 . 因此额外的位使得unsigned int能够存储更大的数字 . 与unsigned int一样11111111111111111111111111111111(二进制)或FFFFFFFF(十六进制)是uint可以存储的最大数字 . 不推荐使用无符号Int,因为如果它们变为负数,则它会溢出并转到最大数量 .
Two's complement非常适合做减法就像添加:)
C和C可以在许多不同的体系结构和机器类型上运行 . 因此,他们可以有不同的数字表示:两个补码,而Ones的补码是最常见的 . 通常,您不应该依赖程序中的特定表示 .
对于无符号整数类型(
size_t
是其中之一),C标准(我认为也是C标准)指定了精确的溢出规则 . 简而言之,如果SIZE_MAX
是size_t
类型的最大值,那么表达式(size_t) (SIZE_MAX + 1)
保证是
0
,因此,您可以确定(size_t) -1
等于SIZE_MAX
. 对于其他无符号类型也是如此 .请注意,上述情况属实:
适用于所有无符号类型,
即使底层机器没有't represent numbers in Two'补充 . 在这种情况下,编译器必须确保标识成立 .
此外,上述意味着您不能依赖签名类型的特定表示 .
编辑:为了回答一些评论:
假设我们有一个代码片段,如:
在
j
的赋值中有一个类型转换 . 假设int
和long
具有不同的大小(大多数[全部?] 64位系统),i
和j
的内存位置的位模式将会有所不同,因为它们具有不同的大小 . 编译器确保i
和j
的值为-1
.同样,当我们这样做时:
正在进行类型转换 .
-1
的类型为int
. 它有一个位模式,但这与此示例无关,因为当转换为size_t
由于转换而发生时,编译器将根据类型的规则转换该值(在本例中为size_t
) . 因此,即使int
和size_t
具有不同的大小,该标准也保证了s
中存储的值将是是size_t
可以采取的最大值 .如果我们这样做:
如果
LONG_MAX
大于INT_MAX
,则i
中的值是实现定义的(C89,第3.2.1.2节) .