c语言关系运算符优先级 C运算符优先级和结合性的合理性理解
当一个表达式有多个运算符时,计算的顺序取决于运算符的优先级和结合性(当有相同优先级时,是较自然的从左到右的左结合,还是反方向的从右到左的右结合?右结合的情形只有前置单目运算符和赋值运算符及复合赋值运算符)。
运算符的优先级和结合性并不是凭空规定的,有其合理性所在。
运算符优先级与结合性的合理性理解,首先确定运算符的大类,运算符构成对应运算符表达式,按大致包含关系可以排列如下:
赋值表达式,右值表达式可以是下列任意表达式构成右值表达式赋值给左值;
逻辑表达式,逻辑表达式通常用于组合关系表达式的逻辑结果;
关系表达式,关系表达式通常可以组合并比较算术表达式,并产生一个逻辑值;
算术表达式,算术表达式通常用来组合字面量或单目运算符表达式;
单目运算符和单个操作数组成的表达式,结合最紧密,具有最高优先级;
当然,逆向包含完全可能,这里只是优先级的合理性理解。
上述操作符构成的表达式的大致包含关系越宽,其优先级越低。
小括号()如果不是用一函数声明,则是是特意用于改变优先级的,自然具有最高的优先级。
赋值运算符,要等一切计算尘埃落定后,再赋值,所以优先级相对较低,也是右结合(右值赋给左值)。
逗号运算符只是分隔作用,所以具有最低的优先级,比赋值运算符还低,结合性是左结合,整体表达式的值落在逗号分隔的最后项上。
逻辑运算符组成的逻辑表达式是用来组合关系表达式的,所以比关系表达式的优先级低。
关系表达式是用来表达关系的,所以待算术表达式运算结束后再比较才是合理的,所以关系表达式的优先级低于算术表达式。
移位运算符是一种特殊的算术运算,所以比算术运算符的优先级稍低一点。
按位与、按位或是一种特殊的关系运算,所以比关系运算符的优先级稍低一点。
条件运算符?:组成的条件表达式结合了关系与赋值运算,优先级高于赋值运算符,低于逻辑运算符也合理。
单目运算符具有最高优先级,后置单目运算符左结合,前置单目运算符右结合,也非常好理解,如:
int arr[] = {0,1,2,3,4,5};
int *p = arr 3;
int days = *--p; //*是前置单目运算符,右结合
printf("%d\n",days); // 2
元素或成员相关的运算符[]、.、->因为要求基址与偏移一起才能构成对内存的引用,相对于其它用于算术运算的操作符(如 、--),自然要求有较的优先级,而域运算符::与变量一起构成内存引用,介于两者之间。
后置运算符 、--与解引用运算符写在一起时,如:
while(*dst = *src );
其优先级和结合性的理解似乎非常令人困惑,其实非常简单,因为后置运算符 、--的引入纯粹就是为了代码的简洁,*dst 只是*dst,dst 的简写,分开后,就不存在优先级和结合性的问题了(虽然后置单目运算符是左结合),并且后置运算符 、--相关的代码一定要从分离的角度去理解
#include <stdio.h>
char* strcpy(char* dst, const char* src)
{
char* start = dst;
while(*dst = *src );
return start;
}
int main()
{
char dst[] = "abcdefdd";
char* src = "xydz";
printf("%s\n",scpy(dst,src));
}
后置运算符 、--操作的操作数是先使用,后操作 、--。其重载时,在改变状态前先存储到临时对象中,然后改变状态,返回之前未改变临时状态的值供使用:
class Point
{
public:
Point(float xx, float yy) :x(xx), y(yy) {}
private:
float x, y;
Point operator (int) //后置
{
Point c = *this; //将加之前的对象保存在临时变量里, 要先使用原值,又要改变其状态,只能使用临时对象了
x = x 1; //修改了对象状态
y = y 1;
return c; //返回改变状态之前的对象先供使用,是临时值,返回的也是值,所以后置不能用做左值
}
Point& operator ()//前置
{
(*this) ; //前面后置 已经写好,所以这里调用的是重载的后置
return *this; //返回当前对象的引用,可以用做左值
}
};
另外,将指针、函数、数组等声明符写在一起构成复合声明时似乎也很难理解,因为函数、数组声明的数据类型与声明符号是写在标识符两端的一种分裂形式,理解起来特别别扭。但可以从单目运算符右结合的角度去理解,可以提炼“右左原则”,从标识符开始,一路向右,走不通了才向左,如:
#include <stdio.h>
int add(int(*arrp)[12],int n)
{
return a b;
}
int main()
{
int(*arrFuncPtr[4])(int(*arrp)[12] ,int);
arrFuncPtr[0] = add;
printf("%d\n",arrFuncPtr[0](3,4)); //31
getchar();
}
用我们的“右左原则”来理解:int(*arrFuncPtr[4])(int(*arrp)[12] ,int i);
从标识符arrFuncPtr开始,一路向右,碰到[],表明这是一个数组,中间的数字4表示数组元素个数是4,然后数组的元素类型是什么呢?
继续向右,碰到右括号")"(这里的括号()是用来改变优先级的,如果是声明函数的(),会先碰到左括号“(“),向左,看到指针声明符*,表明数组元素类型为指针,指针指向的类型是什么呢?
碰到左括号后中止向左,向右,看到左括号,表明是函数声明符,表明数组元素的指针指向的是函数。这里的左括号要找到匹配的右括号,一起构成函数参数:(int(*arrp)[12] ,int i)
函数参数有两个,前面部分较复杂,也可以按右左原则去理解,arrp是一个指针,指向的对象是数组,数组有12个元素,元素的类型是int,arrp是一个数组指针。
综合,arrFuncPtr是一个数组,数组的元素是指针,指针指向的是函数,函数的第一个参数是数组指针。
-End-
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com