*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\n", 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\n", (*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 != ‘\0‘) t++;
 7     while((*t++ = *s++) != ‘\0‘);
 8 }
 9
10 main()
11 {
12     char ss[10] = "ppp",aa[10] = "abcd";
13     fun(ss, aa);
14     printf("%s\n%s\n", ss, aa);
15 }

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

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

关键是下一句

while((*t++ = *s++) != ‘\0‘);

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

步骤如下:

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

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

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

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

最后输出结果为

原文地址:https://www.cnblogs.com/lanhaicode/p/10374941.html

时间: 2024-10-17 02:58:56

*p++与(*p)++与*(p++)------自增运算符常见误区的相关文章

if(xx) a==b 运算符常见误区

If(xx) 1 // 题目1:如下代码输出什么? 2 if ("hello") { 3 console.log("hello") 4 } //true 5 6 // 题目2:如下代码输出什么? 7 if ("") { 8 console.log('empty') 9 }//false 10 11 // 题目3:如下代码输出什么? 12 if (" ") { 13 console.log('blank') 14 }//true

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

自增运算符++有前缀和后缀两种,在搭配间接访问运算符*时,因为顺序.括号和结合关系的影响,很容易让人产生误解,产生错误的结果,这篇文章来详细分析一下这几种运算符的不同搭配情况. ++.--和*的优先级顺序 在C语言运算符的优先级顺序中,后缀的++和--运算符运算优先级16,结合关系是从左到右:简介访问运算符*.前缀++和--运算符运算优先级15,结合关系是从右到左.根据这个关系,可以分析出不同情况下的应用.为了更直观的体现,有以下的例子. 举例说明 有数组a[5],值分别为10,11,12,13

一个由自增运算符以及C语法顺序细节引起的bug

 一.问题描述 在编写modbus代码时发生一件由语法细节引起的bug,起因是自增运算符以及C语法顺序. 输入的数据是2233=0X08B9,高低字节顺序是0x08 0xB9, 使用modbus poll向92寄存器写入十进制数据2233. 但是经过(*reg++)*256+(reg++)之后,结果变成了0xB908. 检查内存也是0xB908. 说明reg_value写入了错误的数. 二.问题调查 反汇编后查看. 1)LDRB R2,[R5],#0X01 --> 把R5数据的低字节放入R2,并

C#自增运算符(++)

一.C#自增运算符(++) 自增运算符(++)是将操作数加1. 1. 前缀自增运算符 前缀自增运算符是“先加1,后使用”.它的运算结果是操作数加1之后的值. 例如: ++x;  // 前缀自增运算符 2. 后缀自增运算符后缀自增运算符是“先使用,后加1”.它的运算结果是操作数加1之前的值. 例如: x++;  // 后缀自增运算符 二.提示 自增运算符(++)适用于数值和枚举类型. 三.示例 using System;using System.Collections.Generic;using

C++重载自增运算符的效率问题

C++规定后缀形式有一个int类型参数,当函数被调用时,编译器传递一个0做为int参数的值给该函数. increment的前缀形式表示“增加然后取回”,后缀形式表示“取回然后增加”. 1 #include "stdafx.h" 2 #include "assert.h" 3 class A 4 { 5 public: 6 A(int i) 7 :m_i(i) 8 { 9 } 10 // ++i 11 A& operator++() 12 { 13 ++m_i

自增运算符

// // main.c // c自增运算符 // // Created by mac on 2019/4/9. // Copyright ? 2019年 mac. All rights reserved. // #include <stdio.h> int main(int argc, const char * argv[]) { int count=1; count++; count=5; int i=8; int y,b=3; y=(++b)+(b++);//8 printf("

printf中的自增运算符

前言 文中均设i=5,汇编代码:movl $5, -4(%rbp) // 将5赋值给rdp向下偏移4个指针的头指针(i) 在装用gcc环境的PC下,可以使用gcc -S -o assembly.S yourcodefile.c 打印汇编代码.首先打印出简单自增运算的汇编代码: i++; 的汇编代码 在早版本的编译器中可能是如下表达形式,而在最新版的gcc中,i++;与++i;的汇编代码是一样的. movl -4(%rbp), %eax // 将i赋值给ax寄存器, ax=5 leal 1(%ra

C语言中指针和自增运算符结合时的运算顺序问题

在C语言中,当指针运算符和++或者–结合时很容易分不清运算顺序,在这里总结一下,下面一共分析6中组合: * p++,(* p)++,* (p++),++* p,++( * p), * (++p). 先看段代码以及输出: #include<stdio.h> int main() { int a[3]={1,3,5}; int *p=a; printf("----------------1----------------\n"); printf("%d\n"

关于自增运算符

昨天参加智邮普创的面试,发现自己c语言好渣啊,先就昨天的一个问题学习一下,然后这周要不停学c来面对如果还有的二面了. 先通过一道面试题引入 int main(void) { int a=3; int b=0; b=(++a)+(++a)+(++a); printf(" %d %d \n", a, b); return 0; } 面试的时候以为答案是15,结果被学长问有没有在电脑上敲过,刚刚敲了一遍发现是16,然后百度了一下大概知道了是怎么计算的.++a是一个有副作用的表达式,这段代码在