一道c语言运算符优先级问题
一道c语言运算符优先级问题
#include <iostream>
using namespace std;
int main()
{
char test[] = {“This is testing.”}, *p = test;
int i,j;
i = 2,j=5;
cout<<*p<<*p++<<endl; // 输出为 : h T
cout<<i<<j<<endl; //输出为: 2,5
cout<<i<<i++<<endl; //输出为:3,2
p = test;
cout<<*p<<*++p<<endl; //输出为:h h
i = 2;
cout<<i<< --i<<endl; //输出为:1 1
i = 2;
printf(“%d,%d”,i,i++); //输出为:3,2
getchar();
return (0);
}
解析
面对:cout<<*p<<*p++<<endl;这样一个语句,初一看输出结果应该是:T h 但是实测结果是 h T ,刚好相反! 为什么呢? 其实这是个运算符优先级问题。
1.要知道不论是 cout<<….<<endl; 还是 printf(); 其参数入栈顺序都是自右至左!,输入时从左至右输出。例如:
cout<<i<<j<<endl; 是先入栈 endl , 紧接着入栈 j值,然后是i值入栈,最后是 cout入栈,然后输出时,是先出i值,然后再出j值,这里还要明白出栈时并不是简单的先出栈cout,然后出栈i,….. 最后出栈endl的, 出栈时就涉及cout库函数怎么操作内存地址的事情,这里完全不用理会,只要知道入栈的顺序是自右至左先入栈j然后入栈i, 出栈时自左至右先出i,再出j值。
2.上面说到入栈是自右至左进行的,其实计算也是自右至左进行的,但是并不是计算一个就入栈一个,而是先自右至左计算全部表达式完毕,然后再自右至左依次入栈。比如:
cout<<*p<<*p++<<endl;
面对这个语句, 计算机是先来计算*p++,发现*p++带有后置运算符,所以第一步就是申请一个临时寄存器Reg1 把当前*p的值暂存到Reg1中,然后执行 后置运算,这里要注意了,这里的指针运算符*和自增运算符优先级是相同的, 现在指针运算符已经使用完了,就是说刚运用指针运算符取出了*p地址下的数据内容,即*p,然后碰到后置运算符++,但是这里就要特别注意了,后置运算符++是对变量进行操作,不会其其它操作符进行操作,所以这里的后置运算符++是应用到了变量p上,而不是*p上,所以 *p++的运算结果是p指向了其下一个地址,注意这里的*p++中的++是对P进行操作的,即操作的是p的地址增加了一个单位,而不是(*p)++,這样表示 ++对(*p)这个数据进行了加1操作,当前的*p数据内容显然是执行第一个字符,这里是指向’t’,那么’t’加1之后,从ascii码表上可知’t’的下一个字符是’u’,而当前环境下,(*p)++实际输出正是’u’。
那么现在可以知道,因为*p++含有后置运算符++,所以计算*p++的结果是将当前p地址下的内容保存到一个临时寄存器Reg1中,然后执行p++,即p地址自增一个单元。
上面计算完*p++之后,接着自右向左走,此时则要来计算*p了,通过前面*p++的计算,此时*p中的p是指向了test数组的第一个值,即指向了’h’字符,即将’h’字符赋值给p地址下的数据存储单位中,然后自右至左走,走到了cout,这里不需要计算,至此全部表达式计算完毕,然后入栈操作, 入栈也是自右至左入栈,所以先入栈*p++那里的值,因为这里有后置运算符,后置运算符就是要先用当前变量的值,然后再进行计算,所以计算之前的值就是代表当前变量在计算这个表达式的值,当然这里只有*p++,所以这个表达式*p++的值就是其进行后置运算之前的保存在临时寄存器Reg1中的值,所以这里入栈*p++表达式式的值其实就是入栈临时寄存器Reg1的值,这里显然是入栈’T’这个字符,即数组的第一个字符。接着自右至左走,入栈*p的值,此时*p的其实就是数组的第二个字符的值,这里显然是’h’。然后入栈cout。 好了现在所有入栈操作已完成, 其入栈顺序是 先入栈endl ,然后是入栈’T’,然后是入栈’h’,最后入栈cout。先入栈的在栈底,后入栈的在栈顶,也就是说endl在栈底,然后是’T’ , ‘T’上面是’h’,然后是’cout’在最上面, 入栈完了之后就调用 cout函数来出栈, 要出栈就是从栈顶走到栈底咯, 现在是cout在栈顶吧,所以调用 cout函数,然后按格式输出,然后cout弹出来了之后, 接下来是遇到’h’字符了, 那么弹出’h’字符, 注意这里就是输出来的第一个字符了,然后接着弹出’T’字符,最后遇到endl,然后再去弹出,发现栈顶指针已经走到栈底指针家了,所以就不弹出了。
现在知道cout<<*p<<*p++<<endl;为什么输出来是 h T 而不是期待的 T h 了吧。
之所以会出现這中情况是是因为cout<<…..<<endl; 是自右向左进行计算和入栈的。 如果先把*p 和*p++的值单独赋值给两个变量,那么输出来的就可以是 T h 了。 如
cout<<i<<j<<endl;
其实也就是说你在计算*p时一定要知道当前这个P执行了谁,它指到哪个地址去了,之所以会出现输出 h T 是因为这个p 在你来计算*p时, 它已经偷偷的指向了其它地方去了。
接着看:cout<<i<<i++<<endl; 这里和上面就是一样了, 知道i初始化为2,那输出则为3,2.
这里要明白:
自增运算符在前面就要运算再使用
自增运算符在后面就是要先使用再运算,因为是先使用再运算,所以在运算之前要用一个临时寄存器或申请一块临时地址把当前值保存起来。
接着看下面 cout<<*p<<*++p<<endl;,应该立刻就知道答案了。
因为++在前所以要先运算再使用,而cout<<….<<endl; 是自右至左运算的,所以会先碰到*++p,然后碰到*p。 先碰到*++p ,因为是前置运算,所以先运算再使用,所以++p 使得p指向了test[]数组的下一个地址值,然后进行指针运算,即取出当前地址下的数据内容。由p = test知,p是指向test[]数组的首地址,++p之后,p执行了test[]数组中的第二个值,即test[1],此时取出这个值,即*p , 此时即完成*++p,接着自右至左走,来到了*p,这个也是应指针运算符取出当前p地址中的数据内容,而p还是指向test[1],所以*p还是取出test[1]的值。所以最终输出的的结果就是 test[1], test[1],即 h h.
p = test;
cout<<*p<<*++p<<endl; //输出为:h h
下面的这两个也就可以同理分析了。
i = 2;
cout<<i<< --i<<endl; //输出为:1 1
i = 2;
printf(“%d,%d”,i,i++); //输出为:3,2
总结
1.碰到指针时,要知道当前操作的指针到底指向内存中的哪个地址,指向哪个变量!
2.声明指针时,要立即赋值为NULL,如 char *ptr = NULL; 如果是 char *ptr; 则会造成ptr指向不明,变成野指针。
3.要知道函数参数的计算方向,一般都是自右向左进行的。如cout<<endl; 因为只有这样才能保证出栈时的顺序和书写时的顺序一致! 比如你现在入栈是自左向右入栈,那现在第一个值被入栈到栈底了, 然后接着入栈其它的内容,最后要出栈的时候,你是要先取出第一个值吧,但是这个值被按自左向右入栈已经入栈到栈底了,這样你要取出来不是多麻烦的,自右向左入栈就要简单多了,直接出栈就可以了,因为最上面的就是第一个,然后这里的自右向左入栈的和计算的方向,不仅仅是满足这个方便,在c/c++语言中這样做还可以满足其确定动态参数的个数等功能。
一道c语言运算符优先级问题的更多相关文章
- C语言运算符优先级总结
一 写在开头1.1 本文内容本文内容为C语言中运算符优先级的总结.转载于:https://blog.csdn.net/huangblog/article/details/8271791,感谢原作者的付 ...
- C语言运算符优先级和ASCII表
1. C语言运算符优先级及结合性 优先级 运算符 名称或含义 使用形式 结合方向 说明 1 [] 数组下标 数组名[常量表达式] 左到右 -- () 圆括号 (表达式)/函数名(形参表) -- . 成 ...
- C语言运算符优先级及结合性
今天去翻了下C语言运算符的优先级和结合性,发现当初学习的时候就没认真记住,惭愧.发现一篇讲得不错的文章,编辑了下转来供以后翻阅. C语言运算符优先级表(由上至下,优先级依次递减) 运算符 结合性 () ...
- (转)C语言运算符优先级 详细列表
C语言运算符优先级 详细列表 文章转自:Slyar Home 优先级 运算符 名称或含义 使用形式 结合方向 说明 1 [] 数组下标 数组名[常量表达式] 左到右 () 圆括号 (表达式)/函数 ...
- C语言 运算符优先级和结合方向
运算符优先级和结合方向 初级运算符( ).[ ].->.. 高于 单目运算符 高于 算数运算符(先乘除后加减) 高于 关系运算符 高于 逻辑运算符(不包括!) 高于 条件运算 ...
- C语言运算符优先级和结合性一览表
所谓优先级就是当一个表达式中有多个运算符时,先计算谁,后计算谁.这个其实我们在小学学算术的时候就学过,如1+4÷2. 但是C语言中的运算符已经远不止四则运算中的加减乘除了,还有其他很多运算符.当它们出 ...
- C语言运算符优先级和口诀(转)
一共有十五个优先级: 1 () [] . -> 2 ! ~ -(负号) ++ -- &(取变量地址)* (type)(强制类型) sizeof 3 ...
- C语言运算符优先级和口诀 (转)
一共有十五个优先级: 1 () [] . -> 2 ! ~ -(负号) ++ -- &(取变量地址)* (type)(强制类型) sizeof 3 ...
- 转载--C语言运算符优先级和口诀
转载:http://www.cnblogs.com/zhanglong0426/archive/2010/10/06/1844700.html 一共有十五个优先级: 1 () [] . -& ...
随机推荐
- 实验:传输层:TCP协议
一.概述 TCP和UDP处在同一层——运输层,但是它们有很多的不同.TCP是TCP/IP系列协议中最复杂的部分,它具有以下特点: (1) TCP提供 可靠的 数据传输服务,TCP是 面向连接的 .应用 ...
- python操作db2和mysql ,ibm_db
我需要提取mysql和db2的数据进行对比,所以需要用python对其都进行操作. python对mysql进行操作应该没什么问题,就是安装drive后就可以了,在上一篇中有讲安装python-mys ...
- 搭建openvpn 未完成。。。
轻松构建自己的OpenVPN家庭服务器(VMware+Amahi) http://os.51cto.com/art/201107/277146_all.htm 这是教程 不用安装第一步的,直接把下载 ...
- Spring依赖注入三种方式详解
在讲解Spring依赖注入之前的准备工作: 下载包含Spring的工具jar包的压缩包 解压缩下载下来的Spring压缩包文件 解压缩之后我们会看到libs文件夹下有许多jar包,而我们只需要其中的c ...
- WebAPP与原生APP的交互设计区别
WebAPP和原生APP同为移动端,很少有研究这两项的交互区别,最近公司做了一次从原生APP到WebAPP(HTML5 )的移植,故总结一下期间遇到的问题及不同点总结. 从使用场景上,WebAPP用户 ...
- 进制,原码VS补码
进制 十,八,十六进制=>二进制 十进制=>二进制:辗转相除取余,10除2商5余0,5除2商2余1,2除2商1余0,1除2商0余1,So,10d=1010b 八进制=>二进制:每1位 ...
- SSH服务器拒绝了密码,xshell连不上虚拟机怎么办
用Xshell链接虚拟机的时候,出现下面情况: 这是sshd的设置不允许root用户用密码远程登录 解决方案: 修改 vim /etc/ssh/sshd_config 找到# Authenticati ...
- Redis系列(一)——简介及安装
摘自:redis中文网(http://www.redis.net.cn/) 参考:http://www.cnblogs.com/hoojo/p/4466024.html,http://keenwon. ...
- ELK 信息统计分析-1
Aggregations 格式如下: "aggregations"{ //可以简写为aggs "<aggregation_name>":{ //名称 ...
- hyper-v无线网络上外网
这个通过无线网络上外网也是找了很多文章,大部分写的都不详细,没有办法成功,原理就是创建一个虚拟网卡,然后把创建的虚拟网卡和无线网卡桥接,虚拟机中使用创建的虚拟网卡,这里创建的虚拟网卡指的是用hyper ...