前言
在c/c++ 的项目编译时经常会遇到 “comp.c:59:42: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]” 这种错误。作为一个”合格的程序员“ 对这种编译告警,通常的处理是忽略,毕竟大家一致的观点是:只有“warning”不算问题!
下面给出一个小case:
#include <stdio.h> int sum_elements(int a[], unsigned int length) { int i; int result = 0; for (i = 0; i <= length-1; i++) result += a[i]; return result; } int main(int argc, char *argv[]) { int a[] = {1, 2, 3}; int m = sum_elements(a, 0); printf("%d ", m); return 0; }
一路轻车熟路,编译运行,但是得到了一个段错误。。。
root@HF-LEE:/mnt/c/Users/Q/Desktop# g++ -Wall -g aaa.c -o aaa aaa.c: In function ‘int sum_elements(int*, unsigned int)’: aaa.c:7:43: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] for (i = 0; i <= length-1; i++) ~~^~~~~~~~~~~ root@HF-LEE:/mnt/c/Users/Q/Desktop# ./aaa Segmentation fault (core dumped) root@HF-LEE:/mnt/c/Users/Q/Desktop#
这里发生了什么呢,看代码只有一个数组,那必然是越界了!!!(问题分析参考:https://www.cnblogs.com/idorax/p/6881996.html)老鸟都会在这里加上强制类型转换来解决问题。但是问题解决了,下次呢,下次还是会有这样的问题存在!所以我们需要了解下问题的本质,以及如何避免。
沿用知乎里常用的一句话:“先问是不是,在问为什么?”
无符号数带来的问题:
如前面示例所示,无符号数与有符号数比较时带来的问题是最容易忽略的,也有可能是最致命的。如果循环中出现无符号数可能会引起死循环,数组中出现无符号可能会导致数组越界。这当然不能把问题归咎于无符号数,更应该说是有符号数带来的思维定势引起的问题。
无符号数带来的问题通常是在数值比较时,与无符号数溢出时才会体现。分别是默认的类型转换与边界条件检测不正确导致的。
既然无符号会有问题,那么我们是不是可以抛弃呢??? 你可以采用java那套做法,在习惯上从不使用无符号数(java有这种方案),但是你会在默写特定场景遇到问题:如网络编程,串口读写。。。
如何合理的使用无符号数:
这是问题的核心了!下面是我总结的一些内容
1. 在位运算、模运算、回绕溢出利用较多的算法实现中(比如各种加密学算法、编码、压缩算法等)
有符号数的符号位在进行位运算时候会造成一些迷惑,位运算中如果采用无符号数会大大减少处理问题时对语言上的思考,可以更专心关注实际问题。
2. 在网络收发,串口读写时候使用无符号数
TCP/IP 经常遇到无符号数,比如IP的表示,我们可以用 ip2long 把点分十进制 ip 转成一个 unsigned int 来表示,这会带来很大方便。串口读写的流更多的是用 unsigned char ,最常见的一个问题是 unsigned char 可以避免日志输出时候按照有符号输出造成的 ‘0xff’ 迷惑人的前缀。
3. 避免有符号数与无符号数的直接接触,包括比较,运算
无符号数与有符号数比较时,编译器会发出警告。同时编译器内部也存在一套默认的类型转换规则(编译器自动进行,用户无感知)。大致分为3类(如有错误请指正)(说明:在计算机里,负数使用反码表示的)
先顶一下规则:有符号(int),无符号(unsigned int),非无符号(除 int 与 unsigned int外的类型,如char,unsigned char),非有符号(与前面同理)。
有符号与无符号比较:有符号数会转换成无符号数来进行比较(如int 与 unsigned int 比较,int 转换成 unsigned int)。
有符号与非无符号数比较:非无符号转化成有符号(如int 与 unsigned char比较,unsigned char 转换成 int)。
无符号与非有符号数比较:非有符号转化成有符号(如unsigned int 与 char比较,char 转换成 unsigned int)。
4. 不要只因为某个数不可能为负就用无符号数
因为这虽然看起来很合要求,但是当无符号溢出时候带来的问题却很可能致命。