C语言 有符号整型 补码表示的小陷阱

几乎所有的 C语言 入门书籍都会在 最前面的章节里 介绍 数据类型,而且基本都是从 整型 开始(至少我看过的基本都是这样)。不过对这个东西的运用,似乎很多人(包括我)都是粗略的看看就过去了。这里我说一下一个小小的陷阱,姑且算是吧。
C语言标准 要求 这些数据类型必须 至少有这样的取值范围。 即,实际上可以放大范围,只要 类型的取值范围 包含 表中定义的范围即可。

C 数据类型 Min Max
char -127 127
unsigned char 0 255
short int -32,767 32,767
unsigned short int 0 65,535
int -32,767 32,767
unsigned int 0 65,535
long int -2,147,483,647 2,147,483,647
unsigned long int 0 4,294,967,295
long long int -9,223,372,036,854,775,807 9,223,372,036,854,775,807
unsigend long long int 0 18,446,744,073,709,551,615


首先看无符号整数,还是比较好理解的,对于占w位的整型,最大能表示的值为2w - 1(w个1),最小淡然是0了。如果把这个位串看成一个向量,最右边的记为第 0 位,最左边的为 第w - 1 位,则对第i位上的数X[ i ](1 or 0),代表的数 即为 X[ i ] * 2i,将这w个值加起来就是此位串表示的数值(很多人应该都知道,就不多说了), value https://www.it610.com/article/==> X[ w-1 ] * 2 w-1 + X[ w-2] * 2 w-2 + ··· + X[ 0 ] * 2 0 。
但是 有符号整数就比较蛋疼了。因为实际上,绝大多数 机器都采用 补码形式表示有符号整数,即最高位(位串中的第w - 1位)表示权值取负值, valuehttps://www.it610.com/article/==>-X[ w-1 ] * 2w-1 + X[ w-2] * 2w-2 + ··· + X[ 0 ] * 20。
按照C语言标准来讲,正负区间是对称的。而采用补码的计算机,若只看最高位,1表示负数,0表示正数,则根据那个什么排列组合来看,也正好一半对一半吧。 不过可惜,上面说错了,当最高位是0,其余的位也都是0的情况下,表示的是整数 0,不是正数,也不是负数。因此正数比负数要 少一个,【10000.....000】(即, -2w-1)没有对应的正数。最大的正数是【0111111...111111】(即, 2w-1 - 1)。这就是补码的取值区间(最大正数的绝对值 比 最小负数的绝对值 少 1)。 当然,我们最关心的还是这个对我们有什么影响。我没有什么项目经验,就平时自己写点东西,所以也不是很清楚,就不丢这个人了<-_-·>。不过例子倒是有一个。
#include #include int main(void) { int min_int = INT_MIN; int x = -min_int; printf("min_int: %d,x: %d \n", min_int, x); //Result:min_int: -2147483648,x: -2147483648return 0; }// end main.


当你对一个正数取负值时,恰好这个数是 该整型的 最小负值,加个负号和没加是一样的。 那这又怎么样呢? 反正就是代码没能按照我们的意愿去执行,而且估计在大项目里也很难发现吧。
采用补码形式表示有符号整数的计算机:
  • 正负区间不对称。
  • 正数比负数少一个。
  • 最小负数【100...0】没有相应的正数对应。

至于有什么好的解决办法嘛,我这个菜鸟也没什么好说的。我给自己的建议是:在C程序中使用整型的时候,注意值的表示范围吧,当可能出现这种 "极限" 情况的时候,选取可以表示更大范围整数的类型吧。 另外,就算不是为了兼容性,保证只使用C语言标准中给出的数值范围应该算个好习惯吧。

    推荐阅读