一道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. iOS多线程-03-NSOperation与NSOperationQueue

    简介 通过NSOperation与NSOperationQueue的组合也能实现多线程 通常将任务封装成NSOperation对象,并将对象添加到NSOperationQueue中实现 NSOpera ...

  2. iOS开发网络篇—网络编程基础(一)

    一.为什么要学习网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过网络跟外界进行数据交互.数据更新 ...

  3. android textview 设置text 字体

    1.使用不同的字库 mLocalClock.setTypeface(Typeface.SANS_SERIF); Typeface face = Typeface.createFromAsset(get ...

  4. Linux套接字编程

    网络中的进程是如何通信的? 在网络中进程之间进行通信的时候,那么每个通信的进程必须知道它要和哪个计算机上的哪个进程通信.否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行 ...

  5. Head First HTML CSS XHTML笔记

    最近在看点前端的东西,看到了这本入门级的好书 <head></head>中的title和style <q></q> inline元素 在<p> ...

  6. JavaScript Patterns 4.2 Callback Pattern

    function writeCode(callback) { // do something... callback(); // ... } function introduceBugs() { // ...

  7. 教你怎样写自定义IP地址算法

    通过IP地址可以看到算法规律,写成自定义IP地址,也可以把IP地址转为自定格式的IP地址.也可以用于加密一些明文数字.起始次方可自定义(以1次方和0次方为例) a.以下写正反算法(以1次方为最小单位) ...

  8. ERP产品价格成本计算的几个方法(转)

          一般财务计算产品价格又很多方法,我这里做了几个供参考,实体属性主要是编号.数量.价格等,这里就不列出了. /// <summary> /// 先进先出算法 /// </s ...

  9. JSON、使用JSON进行数据交换的基础和原理

    1. JSON 1.1. JSON 1.1.1. 什么是JSON JSON即Javascript对象表示法,是一种现在主流的数据交换格式.之所以应用广泛还是由其简单易读所决定的. 简单,只有六种类型的 ...

  10. Window I/O 完成端口 (Windows I/O Completion Port (IOCP))

    相关对象 IO EndPoint, 所有支持重叠IO(overlapped IO)的设备,比如文件,Winsock,管道等. IOCP, IO完成端口内核对象,可以使用API CreateIoComp ...