zoukankan      html  css  js  c++  java
  • 基础C语言知识串串香4☞注意隐形提升带来的C陷阱

    6217760-7a1d63bdb0bc5b7a.jpg

    文章参考微信公众号[嵌入式软件学习圈]

    注意隐形提升带来的C陷阱

    有过面试经历的同学可能曾碰到如代码清单所示的问题。

    #include <stdio.h>
    int
    main(void)
    {
        int a[] = {1,2,3,4,5,6};
        int i = -1;
        if( i <= sizeof(a) ){
            printf("i <= sizeof(a)
    ");
        }else{
            printf("i > sizeof(a)
    ");
        }
        return 0;    
    }
    

    对代码清单进行初步分析可以得出,sizeof(a) 的返回结果为24,而i的值为-1,因此执行语句“if( i <= sizeof(a))”所返回的结果应该为TRUE,即输出结果为“i<=sizeof(a)”。但实际情况并非如此,其输出结果如图所示。

    $: ./test
       i > sizeof(a)
    $:
    

    那么,究竟是什么原因导致出现这样的输出结果呢?

    其实,要回答这个问题并不难。我们知道sizeof()的返回结果是size_ t类型,而size_t类型是一种无符号整数类型。当有符号整数类型和无符号整数类型进行运算时,有符号整数类型会先自动转化成无符号整数类型(请特别注意这一点)。

    因此,在代码清单中,当i与sizeof(a)进行比较时,即执行语句“if( i <=sizeof(a))", i 会自动升级为无符号整数类型。又因为i的值为-1,在它转换为无符号整数类型后就变成一个非常大的正整数(如-1在32位机器上存储为0xffffff而它被解释为无符号整数时就是4294967295 ),远远大于sizeof(a)的返回结果24。

    为了加深读者的理解,我们再来看一个例子。

    #include <stdio.h>
    int
    main(void)
    {
        int a = 3;
        unsigned b = 4;
        printf("%d
    ", a-b);
        printf("%u
    ", a-b);
        printf("%d
    ", (a-b)>>1);
        return 0;    
    }
    

    在代码清单中,当执行语句“a-b”时,变量a会自动由int类型转换为unsigned int类型,再与变量b执行减法运算(即“a-b"),“a-b"的运算结果为0xffffffff。

    当程序使用“%d”(有符号十进制整数)格式输出时,0xffffffff被转换为-1 ;

    当程序使用“%u”(无符号十进制整数)格式输出时,0xffffffff 被转换为4294967295 ;

    最后,程序执行“0xffffffff>>1”运算时,其运算结果为0x7fffffff。 当程序使用“%d"(有符号十进制整数)格式输出时,0x7ffffff被转换为2147483647。如下所示:

    $: ./test
       -1
       4294967295
       2147483647
    $:
    

    由上面两个例子可以看出,将有符号类型与无符号类型混合使用是很危险的。

    因此,我们一定要小心这个数据转换陷阱,尽量少在代码中少使用无符号类型,以免增加不必要的复杂性。尤其是不要仅仅因为无符号数不存在负值而用它来表示某些数量(如年龄、人口等无负数的值)。

    建议尽量使用像int这样的类型,这样在设计升级混合类型的复杂细节时,就不必担心边界情况了(比如不用担心-1被翻译为非常大的正整数)。如果必须使用无符号类型,则应该在表达式中使用强制类型转换,使操作数均为有符号类型或者无符号类型,这样就不必由编译器来选择结果的类型,从而避免存在潜在错误的可能性。比如,我们可以通过强制转换将代码清单改写为代码清单。

    #include <stdio.h>
    int
    main(void)
    {
        int a[] = {1,2,3,4,5,6};
        int i = -1;
        if( i <= (int)sizeof(a) ){
            printf("i <= sizeof(a)
    ");
        }else{
            printf("i > sizeof(a)
    ");
        }
        return 0;    
    }
    

    在代码清单中,我们将语句“if(i <= sizeof(a))”改成“if( i <= (int)sizeof(a))",也就是通过强制类型将其转换成int类型,因而程序的输出结果为:

    $: ./test
       i <= sizeof(a)
    $:
    

    总结

    • 在设计程序时,应该尽量避免有符号数和无符号数的算数和逻辑运算。
    • 在标准C中,算术和逻辑运算一般会默认采用有符号整形(signed int)或双实型(double)来进行运算,即便你运算符左右的两个值都是字符型或短整形,它也是先将他们提升为整形,然后再运算,最后再截短输出。
    • 注意操作符sizeof的返回类型为size_t(unsigned int),不要试图拿有符号数与他直接进行比较。

    往期热文:
    基础C语言知识串串香(1)

    基础C语言知识串串香(2)

    基础C语言知识串串香(3)


    ===========我是华丽的分割线===========


    更多知识:
    点击关注专题:嵌入式Linux&ARM

    或浏览器打开:https://www.jianshu.com/c/42d33cadb1c1

    或扫描二维码:

    6217760-e6bba06e005d8fe7.jpg

  • 相关阅读:
    波段是金牢记六大诀窍
    zk kafka mariadb scala flink integration
    Oracle 体系结构详解
    图解 Database Buffer Cache 内部原理(二)
    SQL Server 字符集介绍及修改方法演示
    SQL Server 2012 备份与还原详解
    SQL Server 2012 查询数据库中所有表的名称和行数
    SQL Server 2012 查询数据库中表格主键信息
    SQL Server 2012 查询数据库中所有表的索引信息
    图解 Database Buffer Cache 内部原理(一)
  • 原文地址:https://www.cnblogs.com/leon1124/p/14039759.html
Copyright © 2011-2022 走看看