• 基础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

  • 相关阅读:
    C/C++分别读取文件的一行
    (转载)C库函数strtok()
    (转载)C++常量折叠和C语言中const常量对比
    ssh
    ubuntu ufw
    uplevel
    ubuntu lucid source.list
    tail
    socket client with proc
    pack forget
  • 原文地址:https://www.cnblogs.com/leon1124/p/14039759.html
Copyright © 2020-2023  润新知