无符号类型的误导

     近日在写程序时,无意间遇到了错误,经反复推敲和调试,确定了错误就出在无符号类型下,百度许久,总结一下。

 

      相信很多人都知道关于C语言的标准大致有两种,一个是老的K&R C标准,一个是新的ANSI C标准(当然,这也不能算新了),这两种标准关于一些细节方面有很多的不同,但毕竟标准只允许改正,不允许废除,就像intel 8086的段地址:偏移地址的寻址方式也不得不为了兼容性而一直保留着一样,而C语言标准的改变,有人称之为“安静的改变”。

 

 

int main(void) { if(-1 < (unsigned int)1) { printf("ANSI C"); } else { printf("K&R C"); } return 0; }

 

      这段代码可以告诉你,你现在使用的到底是K&R C还是ANSI C,因为虽然-1的位模式是一样的,但是在这两种不同的编译器下的解释确不尽相同,ANSI C将其解释为负数,而K&R C奇怪的将其解释为正数。

 

      说奇怪也不算奇怪,因为都是有标准可以参照和拿来解释的:

 

K&R C采用无符号保留(unsigned preserving)原则,就是当一个无符号类型与int或更小的整形混合使用时,结果类型是无符号类型。这是个简单的规则,与硬件无关。但是,它却会如上面的例子那样,使一个负数丢失符号位。

 

ANSI C采用值保留(value preserving)原则,就是当几个整形操作数像上面例子这样混合使用时,结果类型有可能是有符号数,也可能是无符号数,取决于操作数的类型的相对大小。

 

      我遇到的问题是这样的,参见下面的例子:

#define TOTAL_ELEMENTS (sizeof(arr) / sizeof(arr[0])) int main(void) { int arr[] = {1,2,3,4,5}; int d = -1; /*......*/ if(d <= TOTAL_ELEMENTS - 2) /*......*/ return 0; }

 

      经过调试发现,原本if判断语句应该成立的地方却不是这样的,然后翻阅sizeof()的介绍时发现其返回值类型为size_t,即unsigned int,而if语句在signed int和unsigned int之间进行大小判断时,d被升级为unsigned int类型,这就导致了-1被强制转换成了一个非常大的数,致使表达式为假。

 

     其实要修正这个问题也很简单,进行强制类型转换,即:

if(d <= (int)TOTAL_ELEMENTS - 2)

 

     经过这一次,需要更正一个观点,不要因为某些数不可能出现负数(如年龄)而想当然的将其定义为无符号数,这样可能会在后面的编程中,莫名其妙的出现上次问题。建议只在使用位段和二进制掩码时,才去使用无符号数。应该在表达式中使用强制类型转换,使操作数均为有符号数或者无符号数,不要由编译器去选择结果的类型。

 

 

你可能感兴趣的:(编程,c,百度,语言,编译器)