• C语言杂谈(二)自增运算符++与间接访问运算符*的结合关系和应用模式


    自增运算符++有前缀和后缀两种,在搭配间接访问运算符*时,因为顺序、括号和结合关系的影响,很容易让人产生误解,产生错误的结果,这篇文章来详细分析一下这几种运算符的不同搭配情况。

    ++、--和*的优先级顺序

    在C语言运算符的优先级顺序中,后缀的++和--运算符运算优先级16,结合关系是从左到右;简介访问运算符*、前缀++和--运算符运算优先级15,结合关系是从右到左。根据这个关系,可以分析出不同情况下的应用。为了更直观的体现,有以下的例子。

    举例说明

    有数组a[5],值分别为10,11,12,13,14;有指针p指向a[0];另有指针q和整数b。这里注意的是,每段代码开始前,a数组的值和p指针都会回复初始状态。(在一开始写这些测试代码时,忽视了回复初值的问题,导致很多奇怪的错误,比如p已经不再指向a[0],或者a[0]本身已经改变了。)

    1.后缀++

    p = a;
    q = p++;

    p指向a[1]值为11,q指向a[0]值为10。说明后缀自增运算符,返回的是自增前的值。

    2.前缀++

    p = a;
    q = ++p;

    p指向a[1]值为11,q指向a[1]值为11。说明前缀自增运算符,返回的是自增后的值。

    3.*p++

    p = a;
    b = *p++;

    p指向11,b值为10。此时先执行p++,p自增,指向a[1]。p++的值为自增前的值,即a[0]的地址,a[0]的值赋给b。

    4.*(p++)

    p = a;
    b = *(p++);

    结果与3相同。*与++运算符优先级相同,运算顺序是自右向左。

    5.(*p)++

    p = a;
    b = (*p)++;

    此时虽然结果与3相同,但这时是先取p指向的值即a[0],a[0]自增即a[0]的值变成11,自增是后缀的,返回自增前的值,即10。结果相同,但原理还有很大的差别。

    6. *++p

    p = a;
    b = *++p;

    p指向11,b值为11。先执行++p,p自增,指向a[1],++p的值为自增后的值,即a[1]的地址,a[1]的值赋给b。

    7.*(++p)

    p = a;
    b = *(++p);

    与6结果相同,*与++运算符优先级相同,运算顺序是自右向左。

    8.++(*p)

    p = a;
    b = ++(*p);

    p指向11,b值为11,先取p指向a[0]值为10,a[0]的值自增变成11,前缀自增返回值为11赋给b。

    9. ++*p

    p = a;
    b = ++*p;

    结果同8,*与++运算符优先级相同,运算顺序是自右向左。

    总结

    第一个原则就是前缀值为变化后,后缀值为变化前。第二个原则,*++优先级相同,读的时候从右向左。这些都是本人自己试着总结的,如有错误请多指正,希望对有疑惑的朋友有所帮助。

    完整代码

    /*本程序用来测试前缀自增运算符,后缀自增运算符,取内容符号的运算优先级以及顺序带来的返回值的影响*/
    
    #include "stdio.h"
    
    void myPrint(int n,int j, int k)
    {
        printf("no.%d:p->%d, q->%d
    ", n,j, k);
    }
    void myPrintNew(int n,int j, int k)
    {
        printf("no.%d:p->%d, b->%d
    ", n,j, k);
    }
    void init(int a[5])
    {
        a[0] = 10;
        a[1] = 11;
        a[2] = 12;
        a[3] = 13;
        a[4] = 14;
    }//防止某些测试的操作改变了数组的值,在每次使用数组之前使用初始化函数
    
    
    int main(int argc, char* argv)
    {
        //int a[5] = { 10, 11, 12, 13, 14 }, *p,*q,b;
        int a[5] ,*p, *q, b;
        init(a);
    
        //1.
        init(a);
        p = a;
        q = p++;
        myPrint(1,*p, *q);
        //p指向11,q指向10
        //后缀自增运算符,返回的是自增前的值
    
        //2.
        init(a);
        p = a;
        q = ++p;
        myPrint(2,*p, *q);
        //p指向11,q指向11
        //前缀自增运算符,返回的是自增后的值
        //1,2:阅读顺序:从左到右
    
        //3.
        init(a);
        p = a;
        b = *p++;
        myPrintNew(3,*p, b);
        //p指向11,b值为10
        //先执行p++,p自增,指向a[1],p++的值为自增前的值(见1),即a[0]的地址,a[0]的值赋给b
    
        //4.
        init(a);
        p = a;
        b = *(p++);
        myPrintNew(4,*p, b);
        //结果与3相同
        //*与++运算符优先级相同,运算顺序是自右向左
    
        //5.
        init(a);
        p = a;
        b = (*p)++;
        myPrintNew(5,*p, b);
        //结果与3相同,但这时是先取p指向的值即a[0],a[0]自增即a[0]的值变成11,自增是后缀的,返回自增前的值,即10
        //与8对照
    
        //6。
        init(a);
        p = a;
        b = *++p;
        myPrintNew(6, *p, b);
        //p指向11,b值为11
        //先执行++p,p自增,指向a[1],++p的值为自增后的值(见2),即a[1]的地址,a[1]的值赋给b
    
        //7.
        init(a);
        p = a;
        b = *(++p);
        myPrintNew(7, *p, b);
        //与6结果相同
        //*与++运算符优先级相同,运算顺序是自右向左
    
        //8
        init(a);
        p = a;
        b = ++(*p);
        myPrintNew(8, *p, b);
        //p指向11,b值为11
        //先取p指向a[0]值为10,a[0]的值自增变成11,前缀自增返回值为11赋给b
        
        //9??
        init(a);
        p = a;
        b = ++*p;
        myPrintNew(9, *p, b);
        //结果同8
        //*与++运算符优先级相同,运算顺序是自右向左
    
        return 0;
    }

    运行结果

    QQ图片20160316001557

    补充:

    在C++中,建议除非必须,否则不使用后置版本的递增递减运算符

    其原因是前置版本的递增运算符避免了不必要的工作,把加1后的值直接返回改变了运算对象。与之相比,后置版本需要先存储自增前的值,以便返回未修改的值。这是一种浪费。

  • 相关阅读:
    问题:Controller中Response的用法
    C#中字符串前缀@和$
    C#中Viewbag和ViewData的区别
    C#中Session和Cache的区别
    try catch捕获异常
    C#开发中,添加错误日志功能,并自定义错误页面
    数据库索引中,聚集索引和非聚集索引有何区别?
    String和StringBuilder的区别?
    C#函数,引用类型作为值参数,改变参数值后,形参也会改变.重新给实参赋值后,形参则不会改变.
    jQuery操作radio选中和取值
  • 原文地址:https://www.cnblogs.com/yatesxu/p/5281863.html
Copyright © 2020-2023  润新知