本文内容参考《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’。

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

  1. c语言 地址+1,C语言中,为什么指针表达式的值+1.对应的地址值却+4?/为什么两个数组元素的地址相减之差不为地址之差?...

    在C语言中,我们常常用到的一个运算是让某个变量的值+1. 例如 M = M + 1. 而在实际运用中,我们发现 对于指针进行+1运算,算出来的结果是+4. 如下图 图中我们定义的 变量M 和指针Mat ...

  2. 【 C 】关于变量、地址、指针变量等关系的一点思考

    学习C语言,我们永远绕不开的就是指针,指针是一项伟大的发明,但是它给我们的理解也造成了困难.下面是我对变量.指针变量等的一些简单的思考. 变量 谈起变量,或许我们都不屑一顾,可是认真一想,发现坏了,变 ...

  3. 【 C 】最容易误判的优先级问题

    这篇博文有没有必要呢? 关于优先级问题,其实也不必太强求,我们提倡使用()来代表优先级,让程序简单易懂,对自己以及对他们都好.可是呢?有的时候你不得不阅读一些劣质的代码,把一些表达式硬生生地写成了玄学 ...

  4. 程序员认知(一)数据、变量、数据类型、内存、表达式、指针、算法、数据结构、拆箱装箱

    目录: 数据 变量 数据类型 变量.对象与内存 赋值运算符 指针 算法 数据结构 第一个认知:数据 从<C#入门经典>(第8版)中引用一句话: 计算机程序最基本的描述也许是"一系 ...

  5. C++ 笔记(14)— 指针(指针声明、取地址、取值、new/delete、NULL指针、指针运算、指针数组、数组指针、指针传递给函数、从函数返回指针)

    1. 声明指针 指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址.就像其他变量或常量一样,您必须在使用指 针存储其他变量地址之前,对其进行声明. 指针变量声明的一般形式为: type * ...

  6. 《C和指针》对于数组这一节的总结

    <C和指针>对于数组这一节的总结,感觉总结的很精炼,多读有益! 在绝大多数表达式中,数组名的值是指向数组第一个元素的指针.这个规则只有两个例外: sizeof返回整个数组所占用的字节而不是 ...

  7. 谈C/C++指针精髓

    2019独角兽企业重金招聘Python工程师标准>>> [摘要]   指针是C和C++语言编程中最重要的概念之一,也是最容易产生困惑并导致程序出错的问题之一.利用指针编程可以表示各种 ...

  8. c/c++ 函数、常量、指针和数组的关系梳理

    压力才有动力,15年中旬就要准备实习,学习复习学习复习学习复习学习复习--无限循环中,好记性不如烂笔头--从数组开始,为主干. c 的array由一系列的类型相同的元素构成,数组声明包括数组元素个数和 ...

  9. 2017年 1月 15日 指针 学习整理

    有关指针的概念: 指针是一个特殊的变量,它里面存储的数值被解释为内存里的一个地址. FIrst of all:我们需要明确目标 关于指针的学习以及使用我们需要搞清楚有关指针的四个内容:指针的类型,指针 ...

最新文章

  1. tenflow数据集_计算机视常用的数据集 Data sets
  2. pytorch报错Unable to get repr for
  3. C# WInForm中 窗体的this.width和this.height的属性值不能大于显示器的最大分辨率
  4. 【C# 委托 Lambda表达式】一个简单的例子
  5. 2020牛客NOIP赛前集训营-提高组(第三场)C-牛半仙的妹子Tree【虚树,最短路】
  6. hbase本地调试环境搭建
  7. ArcGIS API for JS4.7加载FeatureLayer,点击弹出信息并高亮显示
  8. 基于itchat实现微信群消息同步机器人
  9. tcpdump截wireshark可以打开的包
  10. 重构java和js版_重构Javascript代码示例(重构前后对比)
  11. cloc统计代码行数
  12. 在Chrome浏览器添加IDM插件——顽强版
  13. elementUi上传视频组件
  14. 阿里云RDS-NAS-OSS
  15. ROS激光雷达导航调试记录
  16. 安卓界面UI设计的尺寸标注问题
  17. 使用ttf文件造成的内存泄露
  18. redis集群介绍与搭建
  19. 来了,Github 终于上线收藏夹了
  20. 乐视腾讯深度合作 超级电视26日易迅网首发

热门文章

  1. java设计模式 观察者模式_理解java设计模式之观察者模式
  2. hadoop job 未跑满资源_2018年第26周-解剖MapReduce Job
  3. linux ubuntu下怎样将pdf格式文件转换为doc格式文件,Ubuntu环境下把word文档转成pdf,把pdf文件转成jpg...
  4. revit建筑样板_Revit出建筑施工图步骤及注意事项
  5. php utf-8读取,PHP读取文件,解决中文乱码UTF-8的方法分析
  6. zblog php伪静态,ZBLOG PHP版本Apache和Nginx伪静态规则以及设置方法
  7. 索引名 oracle,ORACLE 索引名称矫情
  8. 第十六届智能车竞赛广东省线上比赛第二波来袭
  9. 测试电子负载用于无线充电系统的功能和精度
  10. 一键将Word转换为MarkDown