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

  • 相关阅读:
    Truck History(poj 1789)
    Highways poj 2485
    117. Populating Next Right Pointers in Each Node II
    116. Populating Next Right Pointers in Each Node
    115. Distinct Subsequences
    114. Flatten Binary Tree to Linked List
    113. Path Sum II
    109. Convert Sorted List to Binary Search Tree
    106. Construct Binary Tree from Inorder and Postorder Traversal
    105. Construct Binary Tree from Preorder and Inorder Traversal
  • 原文地址:https://www.cnblogs.com/lanhaicode/p/10374941.html
Copyright © 2011-2022 走看看