结合性有两种,一种是自左至右,另一种是自右至左,大部分运算符的结合性是自左至右,只有单目运算符、三目运算符的赋值运算符的结合性自右至左。
优先级有15种。记忆方法如下:
记住一个最高的:构造类型的元素或成员以及小括号。
记住一个最低的:逗号运算符。
剩余的是一、二、三、赋值。
意思是单目、双目、三目和赋值运算符。
在诸多运算符中,又分为:
算术、关系、逻辑。
两种位操作运算符中,移位运算符在算术运算符后边,逻辑位运算符在逻辑运算符的前面。再细分如下:
算术运算符分
*,/,%高于+,-。
关系运算符中,》,》=,《,〈=高于==,!=。
逻辑运算符中,除了逻辑求反(!)是单目外,逻辑与(&&)高于逻辑或(||)。
逻辑位运算符中,除了逻辑按位求反(~)外,按位与(&)高于按位半加(^),高于按位或(|)。
这样就将15种优先级都记住了,再将记忆方法总结如下:
去掉一个最高的,去掉一个最低的,剩下的是一、二、三、赋值。双目运算符中,顺序为算术、关系和逻辑,移位和逻辑位插入其中。
为什么后缀++比*操作符优先级高却后对p加1?”
——*p++、*++p、++*p和(*p)++中的操作符优先级问题
假设
int
a[10]
p1=a;
那么
*p++=1;
*++p=2;
++*p=3;
(*p)++=4;
分别应该怎么按照优先级别运算?
按照书上的说法++ (后置)
>++(前置) >* 解引用用 > =
等于
*p++ 是否应该是
现算(p++) 在算* 最后 赋值?
求所有的正确的算法
和解答
---------------------------------------------------------------
++(后缀)属于“后缀操作符”,其优先级高于“前缀操作符”。
*
和++(前缀)都属于“前缀操作符”,优先级相同,按从左到右结合的顺序。都高于赋值运算符。
所以:
*p++=1 相当于
(*(p++)) =
1,也就是说++操作于p上,结果为p原来的值,再作*运算,去除引用,再赋为1。总的作用就是把p引用的对象赋值为1,并把p加1。
*++p=2
相当于(*(++p)) =
2,也就是说++操作于p上,结果为p加1后的值,再作*运算,去除引用,再赋值为1。总的作用就是把p加1,然后对其引用的对象赋值为2。
++*p=3
相当于(++(*p)) =
3,也就是说先对p作*运算去除引用,其结果为p引用的对象,再把这个对象+1,结果还是这个对象,再把它赋值为3。这个表达式要求对象的前缀++操作符的返回值为左值。
(*p)++=4
这里有一个强制优等级(括号),它的级别最高,结果就是((*p)++) =
4,也就是先对p作*运算去除引用,其结果为它引用的对象,然后对这个对象作后缀++操作符,结果为这个对象操作前的值(一般是一个临时变量),然后把它
赋值为4,这个表达式要求对象的后缀++操作符的返回值为左值(整数类型是不符合要求的,只对定义了这个操作符的对象类型才有可能符合要求)。
这个问题以C中很难试验出来,在C++中可以用操作符重载的方法看清楚(操作符重载不会改变优先级):
#include
<iostream>
class
test
{
public:
test(){}
test(int){}
test& operator = (const
test&){std::cout<<"Assignment
of test" << std::endl; return
*this;}
test& operator ++ (){std::cout
<< "Prefix ++ of test"
<< std:: endl; return *
this;}
test& operator ++ (int) {std::cout
<< "Suffix ++ of test"
<< std::endl; return
*this;}
};
class
testptr
{
test
Value;
public:
testptr& operator = (const
test&){std::cout<<"Assignment
of testptr" << std::endl; return
*this;}
testptr& operator ++ (){std::cout
<< "Prefix ++ of testptr"
<< std:: endl; return *
this;}
testptr& operator ++ (int) {std::cout
<< "Suffix ++ of testptr"
<< std::endl; return
*this;}
test& operator
*(){std::cout<< "Operator * of
testptr"<<std::endl; return
Value;}
};
#define
TRACK(X) std::cout
<<std::endl<<"*****
"<< #X
<< " *****"
<<std::endl; X
int
main()
{
testptr p;
TRACK(*p++=1);
TRACK(*++p=2);
TRACK(++*p=3);
TRACK((*p)++=4);
std::cin.get();
}
输出为
*****
*p++=1 *****
Suffix ++ of
testptr
Operator * of
testptr
Assignment of
test
*****
*++p=2 *****
Prefix ++ of
testptr
Operator * of
testptr
Assignment of
test
*****
++*p=3 *****
Operator * of
testptr
Prefix ++ of
test
Assignment of
test
*****
(*p)++=4 *****
Operator * of
testptr
Suffix ++ of
test
Assignment of
test
int p =
1;
int a =
p++;
结果a=1,并不是因为后缀++优先级低
(我记得有一本C教材就是这样写的,真是误人子弟),而是由后缀++的语义决定的。标准的后缀++应该是
“对操作对象做+1操作,并返回操作前的值”,它在赋值运算前运算了,但它的返回值并不是p,而是p在做+1运算前的值。因此我们还可以知道,p++的返
回值应该不是一个左值,p++=a是无法编译通过的。而前缀++则不一样,++p的含义就是“对p做+1运算,并返回p”,其返回值就是p本身(引用),
是一个左值,++p
= a是可以编译的(但没什么意义)。
如果用代码描述一下这两个操作符,应该是这样的:
const int
int::operator ++(int)//后缀++
{
int temp
= *this;
*this =
*this +1;
return
temp;
}
int&
int::operator ++()//前缀++
{
*this =
*this + 1;
return
*this;
}
补充:
在C中,上述语句含义分别是:
*p++=1; -->
temp = p + 1; *temp = 1;
*++p=2;
--> p = p +1; * p = 1;
++*p=3;
--> *p = *p + 1; *p = 3;
(*p)++=4;//语法错误,无法写出对应的语句。
由于后缀增/减量操作符的特殊性,导致初学
者很难理解“为什么后缀的++优先级高却后对变量加1呢?”事实上,事实上,“后缀++”并不是后对变量加1,而是先做了,只是它的返回值并不是这个变
量,而是这个变量改变前的值。如果对它很难理解,建议不要使用这几个操作符,而改用普通的加/减法操作符:
*p++=1; -->
*p = 1; p = p + 1;
*++p=2;
--> p = p + 1; *p = 2;
++*p=3;
--> *p = *p + 1; *p = 3;
(*p)++=4;//语法错误,无法写出对应的语句。
由于在C++中这几个操作符不再是整数和指
针类型特有的,而是可以为类定义的,并且它们可以和+/-1操作语义不同或根本没有加/减法操作符(如双向迭代器),不能简单地用加/减法代替。不过
C++程序员们可以通过看比较好的类的操作符重载函数(如迭代器的)代码真正明白后缀增/减量操作符的语义,不会因为它“优先级高却后对变量加1”的问题
糊涂。不过,仅管如此,我还是认为使用增/减量操作符时最好让一个表达式中或者没有增/减量操作符,或者只有一个增/减量操作符,如:++p;*p
= 1;(相当于*++p = 1)或*p =
1;++p;(相当于*p++=1),这样也不用去区分前缀和后缀的区别了。