快捷搜索:  汽车  科技

c语言中运算符优先级和结合性(C运算符优先级和结合性的合理性理解)

c语言中运算符优先级和结合性(C运算符优先级和结合性的合理性理解)当然,逆向包含完全可能,这里只是优先级的合理性理解。单目运算符和单个操作数组成的表达式,结合最紧密,具有最高优先级;逻辑表达式,逻辑表达式通常用于组合关系表达式的逻辑结果;关系表达式,关系表达式通常可以组合并比较算术表达式,并产生一个逻辑值;算术表达式,算术表达式通常用来组合字面量或单目运算符表达式;

当一个表达式有多个运算符时,计算的顺序取决于运算符的优先级和结合性(当有相同优先级时,是较自然的从左到右的左结合,还是反方向的从右到左的右结合?右结合的情形只有前置单目运算符和赋值运算符及复合赋值运算符)。

运算符的优先级和结合性并不是凭空规定的,有其合理性所在。

c语言中运算符优先级和结合性(C运算符优先级和结合性的合理性理解)(1)

运算符优先级与结合性的合理性理解,首先确定运算符的大类,运算符构成对应运算符表达式,按大致包含关系可以排列如下:

赋值表达式,右值表达式可以是下列任意表达式构成右值表达式赋值给左值;

逻辑表达式,逻辑表达式通常用于组合关系表达式的逻辑结果;

关系表达式,关系表达式通常可以组合并比较算术表达式,并产生一个逻辑值;

算术表达式,算术表达式通常用来组合字面量或单目运算符表达式;

单目运算符和单个操作数组成的表达式,结合最紧密,具有最高优先级;

当然,逆向包含完全可能,这里只是优先级的合理性理解。

上述操作符构成的表达式的大致包含关系越宽,其优先级越低。

小括号()如果不是用一函数声明,则是是特意用于改变优先级的,自然具有最高的优先级。

赋值运算符,要等一切计算尘埃落定后,再赋值,所以优先级相对较低,也是右结合(右值赋给左值)。

逗号运算符只是分隔作用,所以具有最低的优先级,比赋值运算符还低,结合性是左结合,整体表达式的值落在逗号分隔的最后项上。

逻辑运算符组成的逻辑表达式是用来组合关系表达式的,所以比关系表达式的优先级低。

关系表达式是用来表达关系的,所以待算术表达式运算结束后再比较才是合理的,所以关系表达式的优先级低于算术表达式。

移位运算符是一种特殊的算术运算,所以比算术运算符的优先级稍低一点。

按位与、按位或是一种特殊的关系运算,所以比关系运算符的优先级稍低一点。

条件运算符?:组成的条件表达式结合了关系与赋值运算,优先级高于赋值运算符,低于逻辑运算符也合理。

单目运算符具有最高优先级,后置单目运算符左结合,前置单目运算符右结合,也非常好理解,如:

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-

猜您喜欢: