zoukankan      html  css  js  c++  java
  • *p++与(*p)++与*(p++)------自增运算符常见误区

     自增运算符(++)

    自增自减运算符分为前缀形(++a)和后缀形(a++),这里重点分析自增

    大部分人对前缀和后缀的理解一般是,前缀形式是先++再使用(先变后用),后缀形式是先使用再++(先用后变)

     

    (tips:自增运算符只能作用于变量,而不能作用于变量或表达式,例:(i+j)++就是非法的)

    先来说一下一般情况

    1 main()
    2 {
    3     int a = 3;
    4     int b;
    5 
    6     b = a++;
    7     printf("%d", b);
    8 }

     上面这种应该大部分人都会,属于常规情况,是先把a的值赋值给b,再a++,最后输出值为3(大多数应该都是这么理解的)

    但是按照优先级的说法,这里就不太好解释,'++'的优先级比'='高,按理说'++'应该是在'='之前发生的,但表面上看来是'='先与'++'发生

    类似的有

    1 main()
    2 {
    3     int a = 3;
    4     int b;
    5     b = -a++;
    6     printf("%d
    ", b);
    7 }

    按照运算符优先级和结合性的说法,取反运算符'-'和'++'运算符属于同一优先级,结合性是右结合

    所以-a++应该等价于-(a++),但是实际运行结果是-3

     

    笔者在网上找了一些解释,如下

    第一种解释:

    (图片来源:http://forum.ubuntu.org.cn/viewtopic.php?t=301915

    第二种解释:

    (图片来源:https://blog.csdn.net/SunXiWang/article/details/78553933

     第三种解释:

    (图片来源:https://www.cnblogs.com/weiyinfu/p/4836334.html

    上述三种解释中第二种和第三种解释很相似,笔者也比较倾向于第二种和第三种解释

    按照这种解释方式来对上面的代码,重新梳理思路

    1 int a = 3;
    2 int b;
    3 b = a++;
    4 printf("%d", b);

    这里b=a++的确是,先a++然后再赋值,但是a++后的返回值是改变之前的值,

    可以把a++理解为一个函数,函数的返回值是改变之前的值

    1 int temp = a;
    2 a = a+1;
    3 return temp;

    这样用优先级来解释就说得通了,并且这个返回值是一个常量不是变量例如就是错误的

    下面将自增运算符引入一些更复杂的表达式中

    1 main()
    2 {
    3     char *p = "hello";
    4 
    5     printf("%c", *p++);
    6 }

    按照优先级来解释,'*'和'++'属于同一优先级,结合性为右结合,所以说*p++等价于*(p++),先地址++,然后返回改变前的地址,然后*对p解引用得到p[0]的值,输出值应该为h

    (注意:这里很容易误解为括号优先内的地址先++,然后取移动后值,不要被括号误导了,在这里*p++和*(p++)效果是一样的)

    现在我们来对*(p++)进行验证,代码如下

    1 main()
    2 {
    3     char *p = "hello";
    4 
    5     printf("%c", *(p++));
    6 
    7 }

     运行结果为,到这里可以确定*p++和*(p++)效果相同

    (tips:上面的代码char *p,不能改成char p[10]之类的char型数组,因为对char p[10]来说p指的是p[10]的首地址,是一个常量,常量值是不可修改的,如果还这么写编译器会报错,

    现在进入下一个阶段,下面的代码就有点迷惑性了,请读者注意

    1 main()
    2 {
    3     char p[10] = "hello";
    4 
    5     printf("%c", (*p)++);
    6 }

    (这里不能再用char *p了,因为用char *p的话,"hello"就是常量,常量的值是不可更改的,继续用(*p)++的话,编译不会报错,但是程序无法运行) 

    请读者想一下输出的结果应该是什么?这里用括号()将*p括起来了,括号优先级最高,*先与p结合即解引用得到p[0]的值,看起来输出结果应该是i,常规思维一般是p[0]+1即值+1

    但实际的输出结果是

    表面上看起来貌似括号没有起作用,其实不然,现在重新理解下(*p)++的过程

    第一步:括号优先级最高*与p结合,解引用得到p[0]的值

    第二步:*p的值(也就是p[0]的值)++,这里p[0]的值的确是+1了,但是返回值是+1之前的值,%c打印的返回值所以为h

    到这里,就能够解释为什么输出的值为h而不是i了

    再用下述代码对p[0]值进行检查

    1 main()
    2 {
    3     char p[10] = "hello";
    4 
    5     printf("%c***%c
    ", (*p)++, *p);
    6 }

     运行结果为

    这里的*p++可以改为p[0]++,效果是一样的(到这里读者应该基本明白了*p++、*(p++)、(*p)++的区别和运算顺序)

    下面进入最终阶段,先上完整代码

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 
     4 void fun(char *t, char *s)
     5 {
     6     while(*t != '') t++;
     7     while((*t++ = *s++) != '');
     8 }
     9 
    10 main()
    11 {
    12     char ss[10] = "ppp",aa[10] = "abcd";
    13     fun(ss, aa);
    14     printf("%s
    %s
    ", ss, aa);
    15 }

      在继续往下看之前,读者可以先想一下输出的结果应该是什么?

    上述代码中,将ss和aa数组的首地址传入fun函数中,ss和aa分别用char *t和char *s接收,然后while(*t != '')t++;就是将t指向ss数组的最后一个位置的地址该位置存储的是'',

    关键是下一句

    while((*t++ = *s++) != '');

    先分析下(*t++ = *s++),'*'和'++'的优先级相同,而且都是右结合,即等价于*(t++) = *(s++);

    步骤如下:

    第一步:t先与++结合,地址+1,此时t的值已经改变,但是t++返回值的是t+1前的值,即t[3]的地址(&t[3])

    第二步:*与t++的返回值结合,得到t[3]的值(注意这里说的是返回值,而不是*与t结合--虽然不知道这样说有没有问题,为了方便理解先这样说)

    第三、四步:这里*s++的操作与*t++的操作相同这里就不再赘述了

    第五步:将*s的值赋给*t,然后判断*t是否不等于''

    最后输出结果为

    补充:C/C++ 语言的规定告诉我们,任何依赖于特定计算顺序、依赖于在顺序点之间实现修改效果的表达式,其结果都没有保证。程序设计中应该贯彻的规则是:

    如果在任何“完整表达式”(形成一段由顺序点结束的计算)里存在对同一“变量”的多个引用,那么表达式里就不应该出现对这一“变量”的副作用。否则就不能保证得到预期结果。

    参考链接:

    https://bbs.csdn.net/topics/370153775

    http://forum.ubuntu.org.cn/viewtopic.php?t=301915

    https://blog.csdn.net/SunXiWang/article/details/78553933

    https://blog.csdn.net/studyvcmfc/article/details/5630674

    https://blog.csdn.net/frank_jb/article/details/52244318

    https://bbs.csdn.net/topics/390722765

    https://www.cnblogs.com/weiyinfu/p/4836334.html

    最后提一下一个比较坑的地方:int a  = 0; a=a++;这条语句无论执行多少次,a的值是肯定不会变的,但是在Microsoft Visual C++ 2010 Express这个编译器中a的值是在不断变化的(就是一直在+1),在其他在线编译器上结果都是0

    a=a++;相关的分析的链接:深入剖析C函数参数的结合顺序及a++和++a的区别(该篇使用了反汇编对a++、++a进行了分析)

                  a = a++ 与 a = ++a

  • 相关阅读:
    PostMan-NewMan运行参数
    shell脚本学习简单记录笔记
    android开发okhttp-4.9.1源码大致流程解读
    android开发获取键盘高度以及判断键盘是否显示(兼容分屏模式)
    Android开发The style on this component requires your app theme to be Theme.AppCompat (or a descendant)的解决方法
    Linux开发Ubuntu安装man手册
    Android开发源码解读四大组件源码解读简单梳理
    Android开发涉及到的AMS类和ActivityThread类源码解读
    Android开发为什么主线程可以一直运行而不会退出来
    前端CryptoJS加密、后端解密代码实现参考
  • 原文地址:https://www.cnblogs.com/lanhaicode/p/10374941.html
Copyright © 2011-2022 走看看