一道c语言运算符优先级问题

#include <iostream>

using namespace std;

int main()

{

char test[] = {“This is testing.”}, *p = test;

int i,j;

i = 2,j=5;

//print

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语言运算符优先级问题的更多相关文章

  1. C语言运算符优先级总结

    一 写在开头1.1 本文内容本文内容为C语言中运算符优先级的总结.转载于:https://blog.csdn.net/huangblog/article/details/8271791,感谢原作者的付 ...

  2. C语言运算符优先级和ASCII表

    1. C语言运算符优先级及结合性 优先级 运算符 名称或含义 使用形式 结合方向 说明 1 [] 数组下标 数组名[常量表达式] 左到右 -- () 圆括号 (表达式)/函数名(形参表) -- . 成 ...

  3. C语言运算符优先级及结合性

    今天去翻了下C语言运算符的优先级和结合性,发现当初学习的时候就没认真记住,惭愧.发现一篇讲得不错的文章,编辑了下转来供以后翻阅. C语言运算符优先级表(由上至下,优先级依次递减) 运算符 结合性 () ...

  4. (转)C语言运算符优先级 详细列表

    C语言运算符优先级 详细列表 文章转自:Slyar Home 优先级 运算符 名称或含义 使用形式 结合方向 说明 1 [] 数组下标 数组名[常量表达式] 左到右   () 圆括号 (表达式)/函数 ...

  5. C语言 运算符优先级和结合方向

    运算符优先级和结合方向 初级运算符( ).[ ].->..  高于  单目运算符  高于  算数运算符(先乘除后加减)  高于  关系运算符  高于  逻辑运算符(不包括!)  高于  条件运算 ...

  6. C语言运算符优先级和结合性一览表

    所谓优先级就是当一个表达式中有多个运算符时,先计算谁,后计算谁.这个其实我们在小学学算术的时候就学过,如1+4÷2. 但是C语言中的运算符已经远不止四则运算中的加减乘除了,还有其他很多运算符.当它们出 ...

  7. C语言运算符优先级和口诀(转)

    一共有十五个优先级: 1   ()  []  .  -> 2   !  ~   -(负号) ++  --   &(取变量地址)*   (type)(强制类型)    sizeof 3   ...

  8. C语言运算符优先级和口诀 (转)

    一共有十五个优先级: 1   ()  []  .  -> 2   !  ~   -(负号) ++  --   &(取变量地址)*   (type)(强制类型)    sizeof 3   ...

  9. 转载--C语言运算符优先级和口诀

    转载:http://www.cnblogs.com/zhanglong0426/archive/2010/10/06/1844700.html 一共有十五个优先级: 1   ()  []  .  -& ...

随机推荐

  1. eclipse智能提示

    原文地址:http://www.cnblogs.com/myitm/archive/2010/12/17/1909194.html Windows→Preferences→Java→Editor→Co ...

  2. OOD沉思录 --- 类和对象的关系 --- 包含关系3

    4.7 类包含的对象数目不应当超过开发者短期记忆数量,这个数目通常应该是6左右 4.8 让系统在窄而深的包含体系中垂直分布 假设有如下两份菜单: 正餐 --->甜瓜 --->牛排 ---& ...

  3. 《好设计不简单Ⅱ:UI设计师必须了解的那些事》

    <好设计不简单Ⅱ:UI设计师必须了解的那些事> 基本信息 作者: (日)古贺直树 译者: 张君艳 丛书名: 图灵交互设计丛书 出版社:人民邮电出版社 ISBN:9787115363435 ...

  4. 【原创】Java实现手机号码归属地查询

    网络上已经有很多的手机号码归属地查询的API接口,但是这些接口总是有一些大大小小的缺陷. 总结一下这些缺陷: 1.要直接将它的搜索框链接形式粘到自己的页面,点击查询的时候还要跳转到他们的网站来展示归属 ...

  5. JSP过滤器Filter配置过滤类型汇总

    一.配置方法1 映射过滤应用程序中所有资源<filter>    <filter-name>loggerfilter</filter-name>    <fi ...

  6. cocos2d-x之xml文件读取初试

    auto doc=new tinyxml2::XMLDocument(); doc->Parse(FileUtils::getInstance()->getStringFromFile(& ...

  7. 工作中常用的Linux命令:目录

    工作两三年,每天都和Linux打交道,但每每使用Linux命令的时候却会像提笔忘字般不知如何使用,常常查手册或到网上找资料.此系列文章主要是为了方便自己在使用命令时随时可查阅.鄙人才疏学浅,文中若有任 ...

  8. Linux查看BIOS信息

    http://www.linuxde.net/2013/02/12499.html

  9. 如何切入 Linux 内核源代码

    Makefile不是Make Love 从前在学校,混了四年,没有学到任何东西,每天就是逃课,上网,玩游戏,睡觉.毕业的时候,人家跟我说Makefile我完全不知,但是一说Make Love我就来劲了 ...

  10. (转)c++类的成员函数存储方式(是否属于类的对象)---一道面试题引发的思考

    昨天去面试一家公司,面试题中有一个题,自己没弄清楚,先记录如下: class D { public: void printA() { cout<<"printA"< ...