一道c语言运算符优先级问题
一道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++语言中這样做还可以满足其确定动态参数的个数等功能。