【 C 】对指针表达式的个人总结与思考
本文内容参考《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 】对指针表达式的个人总结与思考相关推荐
- c语言 地址+1,C语言中,为什么指针表达式的值+1.对应的地址值却+4?/为什么两个数组元素的地址相减之差不为地址之差?...
在C语言中,我们常常用到的一个运算是让某个变量的值+1. 例如 M = M + 1. 而在实际运用中,我们发现 对于指针进行+1运算,算出来的结果是+4. 如下图 图中我们定义的 变量M 和指针Mat ...
- 【 C 】关于变量、地址、指针变量等关系的一点思考
学习C语言,我们永远绕不开的就是指针,指针是一项伟大的发明,但是它给我们的理解也造成了困难.下面是我对变量.指针变量等的一些简单的思考. 变量 谈起变量,或许我们都不屑一顾,可是认真一想,发现坏了,变 ...
- 【 C 】最容易误判的优先级问题
这篇博文有没有必要呢? 关于优先级问题,其实也不必太强求,我们提倡使用()来代表优先级,让程序简单易懂,对自己以及对他们都好.可是呢?有的时候你不得不阅读一些劣质的代码,把一些表达式硬生生地写成了玄学 ...
- 程序员认知(一)数据、变量、数据类型、内存、表达式、指针、算法、数据结构、拆箱装箱
目录: 数据 变量 数据类型 变量.对象与内存 赋值运算符 指针 算法 数据结构 第一个认知:数据 从<C#入门经典>(第8版)中引用一句话: 计算机程序最基本的描述也许是"一系 ...
- C++ 笔记(14)— 指针(指针声明、取地址、取值、new/delete、NULL指针、指针运算、指针数组、数组指针、指针传递给函数、从函数返回指针)
1. 声明指针 指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址.就像其他变量或常量一样,您必须在使用指 针存储其他变量地址之前,对其进行声明. 指针变量声明的一般形式为: type * ...
- 《C和指针》对于数组这一节的总结
<C和指针>对于数组这一节的总结,感觉总结的很精炼,多读有益! 在绝大多数表达式中,数组名的值是指向数组第一个元素的指针.这个规则只有两个例外: sizeof返回整个数组所占用的字节而不是 ...
- 谈C/C++指针精髓
2019独角兽企业重金招聘Python工程师标准>>> [摘要] 指针是C和C++语言编程中最重要的概念之一,也是最容易产生困惑并导致程序出错的问题之一.利用指针编程可以表示各种 ...
- c/c++ 函数、常量、指针和数组的关系梳理
压力才有动力,15年中旬就要准备实习,学习复习学习复习学习复习学习复习--无限循环中,好记性不如烂笔头--从数组开始,为主干. c 的array由一系列的类型相同的元素构成,数组声明包括数组元素个数和 ...
- 2017年 1月 15日 指针 学习整理
有关指针的概念: 指针是一个特殊的变量,它里面存储的数值被解释为内存里的一个地址. FIrst of all:我们需要明确目标 关于指针的学习以及使用我们需要搞清楚有关指针的四个内容:指针的类型,指针 ...
最新文章
- tenflow数据集_计算机视常用的数据集 Data sets
- pytorch报错Unable to get repr for
- C# WInForm中 窗体的this.width和this.height的属性值不能大于显示器的最大分辨率
- 【C# 委托 Lambda表达式】一个简单的例子
- 2020牛客NOIP赛前集训营-提高组(第三场)C-牛半仙的妹子Tree【虚树,最短路】
- hbase本地调试环境搭建
- ArcGIS API for JS4.7加载FeatureLayer,点击弹出信息并高亮显示
- 基于itchat实现微信群消息同步机器人
- tcpdump截wireshark可以打开的包
- 重构java和js版_重构Javascript代码示例(重构前后对比)
- cloc统计代码行数
- 在Chrome浏览器添加IDM插件——顽强版
- elementUi上传视频组件
- 阿里云RDS-NAS-OSS
- ROS激光雷达导航调试记录
- 安卓界面UI设计的尺寸标注问题
- 使用ttf文件造成的内存泄露
- redis集群介绍与搭建
- 来了,Github 终于上线收藏夹了
- 乐视腾讯深度合作 超级电视26日易迅网首发
热门文章
- java设计模式 观察者模式_理解java设计模式之观察者模式
- hadoop job 未跑满资源_2018年第26周-解剖MapReduce Job
- linux ubuntu下怎样将pdf格式文件转换为doc格式文件,Ubuntu环境下把word文档转成pdf,把pdf文件转成jpg...
- revit建筑样板_Revit出建筑施工图步骤及注意事项
- php utf-8读取,PHP读取文件,解决中文乱码UTF-8的方法分析
- zblog php伪静态,ZBLOG PHP版本Apache和Nginx伪静态规则以及设置方法
- 索引名 oracle,ORACLE 索引名称矫情
- 第十六届智能车竞赛广东省线上比赛第二波来袭
- 测试电子负载用于无线充电系统的功能和精度
- 一键将Word转换为MarkDown