有符号数与无符号数比较的坑

Posted sinpo828

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有符号数与无符号数比较的坑相关的知识,希望对你有一定的参考价值。

前言

在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\\n", m);
        return 0;
}

 一路轻车熟路,编译运行,但是得到了一个段错误。。。

[email protected]:/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++)
                                         ~~^~~~~~~~~~~
[email protected]:/mnt/c/Users/Q/Desktop# ./aaa
Segmentation fault (core dumped)
[email protected]:/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. 不要只因为某个数不可能为负就用无符号数

因为这虽然看起来很合要求,但是当无符号溢出时候带来的问题却很可能致命。

以上是关于有符号数与无符号数比较的坑的主要内容,如果未能解决你的问题,请参考以下文章

有符号数与无符号数之间运算问题探究

02_有符号数与无符号数

无符号数和有符号数的转化

有符号数的加减法 和无符号数的加减法,和,系统是如何识别有符号数和无符号数的

汇编语言无符号数与有符号数转换

有符号与无符号数之间赋值的截断和扩展