【C】对指针表达式的个人总结与思考

版权声明:原创,还是不要转载了吧,如果忍不住要转载,就一定要注明地址哦! https://blog.csdn.net/Reborn_Lee/article/details/82156024

本文内容参考《c 和 指针》。

声明:本博文只为那些能沉得住气,认真研究,探索真知的人参考,浮躁的人请离开,因为看不懂。

感觉以前学c的时候,学的指针真是白学了,今天看到这个内容,困惑后,又让我茅塞顿开。

贴出上篇博文的地址,可供参考:【C】对左值与右值的一些个人思考

说些题外话,由于在查看资料的时候,经常看到博客园中的一些博主的博文,写的也是十分的精彩,于是,尝试注册了一下,准备发几篇博客,可能是由于不熟悉吧,怎么使用怎么别扭。自从CSDN博客改版了以后,写博客也变得很方便了,页面做的越来越精简,重点突出,不得不夸赞下这个平台,真是一个分享知识的殿堂。


进入正题:

有关左值和右值这个话题,我们在上篇博文中也说到了,而有关变量的一点内容,我也写了一篇不太完善的博客:【C】关于变量、地址、指针变量等关系的一点思考,这些都是我最近遇到的问题,知识这个东西,越是琢磨越是有味道,这些都是我以前没有发现的问题,我以前怎么会想到,不起眼的变量,突然让我大脑短路,糊涂了起来。

今天的正题是表达式,其实不管是表达式也好,单独的一个变量也好,它们作为左值和右值使用时,所指示的含义是不一样的,知道这一点,对于变量也会有一个更深刻地理解。


先看一个声明:

char ch = 'a';
char *cp = &ch;

简简单单的两句声明语句,拉开我们今天的讨论。

1、ch

我们都知道ch是一个字符变量,正如声明所言,那里的ch是一个字符变量,且被赋值了一个字符 'a',这里的ch是作为左值使用的,也就是占据着赋值操作符左边的位置。作为左值,它的含义是一个内存的地址,该内存中可以存放字符‘a’;而如果我们将ch作为一个右值使用时,它的含义是什么呢?

很简单,它代表的是该内存中的数值,也即是变量的值,在本例中就是‘a’。

我们在下面可以写这么一行代码:

ch1 = ch;

那么ch1这个字符变量的值就是ch的值,ch作为右值使用,代表的是一个变量值。(当然,ch1要提前声明为一个char型变量)。

综上所述,我们总结:

ch作为右值使用时,代表一个变量值,也即是地址指定的内存中存放的数值。

ch作为左值使用时,代表的是一个地址,ch是为某个地址的一个名字而已,其他的数值可以赋值给ch,也即是存放于该地址指定的内存中去。


2、&ch

这个表达式作为右值代表的是变量ch的地址。

但是它不能作为左值使用,也即是作为左值使用是非法的。为什么呢?

可以这样解释:

当表达式&ch进行求值时,它的结果存放于计算机的什么地方呢?它肯定会位于某个地方,但是你无法知道它存放于何处。这个表达式并未标识任何机器内存的特定位置,所以它不是一个合法的左值。


3、cp

从声明中可以看出cp是一个指向字符变量的指针变量,那么既然它作为一个变量,如果作为左值使用的话,就应该是一个地址了。给它赋值的值应该是一个地址。

如果作为右值使用呢?

作为右值使用,就是cp的值。在这个例子中就是ch的地址值。


4、&cp

这个表达式和第二个情况有点类似,首先,我们知道&操作符不能作为左值使用,因此,这个表达式作为左值就非法的(还可以像&ch那个情况一样解释,这个值的位置并未清晰定义,所以不是为一个合法的左值)。但作为右值的话,这个表达式就是去指针变量cp的地址了。(指针变量也是变量,是变量就有地址。)


5、*cp

往往作为右值更好理解一点,*cp作为右值,也就是cp指向的地址内存中存放的内容,这里易知就是字符‘a’;作为左值就是cp指向的地址,也即是变量ch代表的地址。

对于这个表达式的理解,也可以参考我的博文:【C】对左值与右值的一些个人思考


6、*cp + 1

越来越刺激了,也意味着理解起来越来越有点困难了,加把劲吧。

还是先看作为右值的情况,由于*cp作为右值表示cp指向的地址内存中存放的内容,那么很容易推理出表达式(*cp + 1)表示存放的内容加1,本例中就是字符‘a’+1,不就是字符‘b’吗?

作为左值的情况,我们好好讨论下:

由于*cp作为左值的意思是cp指向的那个地址本身,由于*cp + 1 这个表达式的最终结果的存储位置并未清晰定义,所以它不是一个合法的左值。

也许你可能会问:*cp都可以作为左值,为什么*cp + 1就不能作为左值?

*cp作为左值,指的是cp指向的那个地址本身,本例中指的就是变量ch代表的地址,而*cp + 1,作为左值的话,它的位置并未清晰定义,(这里的清晰定义,例如字符‘a’的位置就定义成了ch,ch代表‘a’存放的内存的位置),没有清晰定义的位置就不能作为左值了。

而操作符+的优先级也证实了,+ 的结果不能作为左值。

这里给出我写的一篇关于优先级的博文:C/C++操作符的优先级和结合性问题浅析


7、 *(cp + 1)

先看作为右值的情况,作为右值cp指的是变量ch的地址,该地址加1,就成了ch之后的那个地址,然后间接访问该地址的内容。

总结上面的一句话就是上述表达式作为右值,访问的是cp指向的位置的下一个位置的值。

作为左值的话,就是cp + 1 这个指针对应的位置本身。

感觉说到这里,有些没看懂的人就会说我胡说八道,前后矛盾了。

这里确实需要说明一下:

指针加法(cp + 1)的结果是个右值,因为它的存储位置并未清晰定义。如果没有间接访问操作符*,这个表达式将不是一个合法的的左值。然而,间接访问跟随指针访问一个特定的位置。这样,*(cp + 1)就可以作为左值使用,尽管cp + 1本身并不是左值。间接访问操作符是少数几个其结果为左值的操作符之一。

上面这段话是书上的原话,我也很无奈呀,可是没办法,这个间接访问操作符就是看的,人家特殊还不行吗?接受吧,骚年!


8、++cp

++和--操作符在指针变量中使用得相当频繁,所以在这种上下文中理解它们是非常重要的。

这里使用的操作符是前缀++,意味着先增加操作数的值,再返回操作后的这个结果。

在这个表达式中,我们增加了指针变量cp的值。表达式的结果是增值后的指针的一份拷贝,因为前缀++先增加它的操作数的值再返回这个结果。所以作为右值而言, 它是ch的地址值加1的地址值。同样由于该位置未清晰定义,故而不能作为左值。

这里声明,前缀--类似。


9、cp++

这个与8作为比较出现,这里使用的是后缀++,后缀++操作符同样增加cp的值,但是它先返回cp的值的一份拷贝再增加cp的值。

因此,作为右值使用的话,它表示cp本身的值,也就是ch的地址值。

那么能不能作为左值呢?

是不能的,因为++的优先级高于=(赋值)操作符,所以,如果cp++作为左值,会对cp进行加1操作后在被赋值,由于cp++代表的地址未有清晰的定义,所以不能作为左值。(cp++作为左值相当于cp+1了,所以是非法的。)


10、*++cp

感觉越来越变态了,我相信贴心的程序员会把上面的表达式写成这个样子*(++cp),(因为++优先级高于*)尽管语法上没有任何差别,但是这样显然更加的清晰。

其实分析了这么多,我想大家如果看懂了上面的东西,这个也不难了,这个肯定可以作为左值使用的。

首先分析作为右值使用的情况,cp作为指针变量的值加1后,也就是ch的地址值加1,得到一个新的地址,加上间接访问符之后,便是取该地址对应的内存中的内容。

作为左值呢?

显然是cp +1指向的那个地址本身,也即是ch的地址加1后的那个新地址本身。

这里再次显现了间接访问操作符将其结果作为左值的强大作用。


11、*cp++

这个和10想比较,由于++的优先级高于*,所以上面的表达式可以这样写,*(cp++),如此以来,便清晰多了。

后缀++的作用不必多言,作为右值的话,固然是先取cp值的拷贝作为结果,之后在进行加1的操作,所以表达式作为右值的结果是取cp指向的地址(即ch代表的地址&ch)的内容(值)。这里也就是字符‘a’。

那作为左值,这个表达式就表示cp指向的地址本身,也即是ch的地址。

综上:使用后缀++操作符所产生的结果与使用前缀++的结果不同,它的右值和左值分别是变量ch的值和ch的内存位置,也即是cp原先所指。

同样,后缀++操作符在周围的表达式中使用其原先操作数的值。

间接访问操作符和后缀++的组合常常令人误解。优先级表格显示后缀++操作符的优先级高于*操作符,但表达式的结果看上去像是先执行间接访问操作。

事实上,这里涉及3个步骤:

(1)++操作符产生cp的一份拷贝;

(2)然后++操作符增加cp的值;

(3)最后,在cp的拷贝上执行间接访问操作。


12、++*cp

说实话,看到这样的表达式未免有些愤怒,这里还好,如果用到程序中去,看起来未免会造成心理负担。所以还是要适当的使用括号来表达自己的意思。

由于++ 和* 的结合性都是从右到左(R-L),所以,先运算*cp,之后在++。

所以上面的表达式可以写成这样:++(*cp);

然后我们分析,作为右值的情况,*cp的意思是去cp指向的地址的值,这里为‘a’,然后执行++操作,也就是加1,那么表达式的值为字符‘b’。

那能不能作为左值呢?

恐怕都说腻了,*cp作为左值的意思是cp指向的那个地址本身,也即是ch代表的地址,在进行++操作,也就是地址加1,得到的新地址未清晰定义,所以不能作为左值。

C/C++操作符的优先级和结合性问题浅析

这篇博文的优先级表格,++ 操作符的结果不能作为左值,也算是作为一种验证了。


问题的所有情况基本上都说的差不多了。事实上可以到此为止了。但是呢?下面还有最后三种终极大变态情况,它们在实际应用中基本上不出现,但是正如《c和指针》这本书上说的一样,对它有一个透彻地理解有助于提高你的技能。

我就提高下自己的技能吧。

13、(*cp)++

简单地说,作为右值,就是先取cp指向的地址的值(本文中为‘a’),然后++,这样得到的就是‘b’。

作为左值,显然是非法的,因为最后执行的是++操作。或者说,*cp作为左值,代表cp指向的地址本身,在进行++,得到的是一个未清晰定义的地址,不能作为左值。


14、++*++cp

看到这个表达式,我第一件事情是喝了口水让自己冷静下来,别冲动,别冲动,所以也请大家不要冲动。

冷静下来之后,可以用括号来划分下层次,得知可以表示成等价形式++(*(++cp))。

由于最后进行的是++操作,所以不能作为左值。

下面看作为右值的情况,先进行++cp操作,是cp的地址加1得到的一个新地址(&ch + 1),之后进行间接访问操作,取新地址对应的内存存储的的值,再次进行++,是对这个新地址对应的内存中的值加1.


15、++*cp++

这个更需要冷静!真的是最后一个了。

*的优先级低于++,所以加上第一个括号: ++*(cp++)

结合性都是自右向左,第二个括号:++(*(cp++))

同样,不能作为左值使用,因为最后操作的是++。

那么作为右值的话,cp++中使用的是后缀++,故参与表达式运算的是cp的一份拷贝,之后再进行cp加1操作,*(cp++)取cp指向的地址的内容,即‘a’,之后对‘a’加1得到‘b’。

猜你喜欢

转载自blog.csdn.net/Reborn_Lee/article/details/82156024