表达式(三)
三、后缀运算符
后缀表达式(postfix expression)包括数组下标表达式([ ])、函数调用表达式(( ))、结构和联合成员表达式(.和->)、后缀自增表达式和后缀自减表达式(++和--)、复合字面量(compound literals)。
1、数组下标
数组下标表达式的语法格式如下所示:
postfix-expression[expression]
其中postfix-expression是后缀表达式,应为指向完整对象类型的指针;expression是整数类型表达式;整个数组下标表达式的类型为后缀表达式指向对象的类型;该表达式是数组元素的下标表示。
对于声明int arr[5];,arr是指向int类型对象的指针;arr[3]的类型是int类型。
下标运算符([ ])的定义:E1[E2]等同于(*((E1)+(E2)))。由于转换规则适用于加法运算符,如果E1是数组对象(等同于指向数组初始元素的指针。),E2是整数,则E1[E2]表示数组E1的第E2个元素(从0开始计算。)。
int arr[] = {3, 6, 9, 8, 5}; printf("%d\n", arr[2]); //输出9。 printf("%d\n", (*((arr)+(2)))); //输出9。
连续数组下标运算符指定多维数组元素;例如:arr[2][3]表示二维数组元素;arr[2][3][4]表示三维数组元素。如果E是一个维度为i×j×···×k的n维数组(n≥2),则E(用作非左值。)转换为指向维度为j×···×k的(n - 1)维数组的指针。如果该指针显式地用作间接运算符*的操作数(例如:*(E)),或者隐式地用作下标运算符([ ])的操作数(例如:E[0]),结果是引用(n-1)维数组;如果该结果用作非左值,该结果本身会转换成指针。由此可见C语言中数组是按行主序(row_major order)存储的(即最后一个下标变化最快。)。
下面的例子演示了使用数组名arr,而不使用数组下标,访问二维数组元素arr[1][2]的过程。
int arr[3][4] = {[1][2]=5}; /*第一步初始位置。*/ printf("arr: %p\n", arr); //输出arr: 0xffffcc10。 /*第二步移动到第二行。*/ printf("arr+1: %p\n", arr+1); //输出arr+1: 0xffffcc20。 printf("*(arr+1): %p\n", *(arr+1)); //输出*(arr+1): 0xffffcc20。 printf("arr[1]: %p\n", arr[1]); //输出arr[1]: 0xffffcc20。 /*第三步移动到第二行第三个元素。*/ printf("*(arr+1)+2: %p\n", *(arr+1)+2); //输出*(arr+1)+2: 0xffffcc28。 printf("*(*(arr+1)+2): %d\n", *(*(arr+1)+2)); //输出*(*(arr+1)+2): 5。 printf("arr[1][2]: %d\n", arr[1][2]); //输出arr[1][2]: 5。
上述声明中,arr是一个int类型的维度为3×4的二维数组;更准确地讲,arr是一个由3个元素构成的数组,其中每个元素都是由4个int类型元素构成的数组。
arr转换成指针后,其值是数组首字节的地址;等于数组首元素的地址值(&arr[0]);等于二维数组首元素的地址值(&arr[0][0]);arr[0][0]是数组arr[0]的首元素,所以arr[0]和&arr[0][0]的值相等。表达式arr、&arr[0]、arr[0]、&arr[0][0]只是数值相等,但类型不同,其中arr和&arr[0]是兼容类型,arr[0]和&arr[0][0]是兼容类型,两组类型相互之间并不兼容。
arr+1指向第2个数组元素arr[1],*(arr+1)获取第2个数组元素arr[1]的值(*(arr+1)与arr[1]等价。);*(arr+1)+2指向二维数组第2行第3个数组元素arr[1][2],*(*(arr+1)+2)获取二维数组第2行第3个数组元素arr[1][2]的值(*(*(arr+1)+2)与arr[1][2]等价。)。
表达式*(arr+1)+2中1和2的单位字节数是不一样的。加1将增加1×4×sizeof(int)个字节;加2将增加2×1×sizeof(int)个字节。