C语言的算符优先级

C语言中提供许多算符,比如算术运算、逻辑运算、关系运算等,必须规定它们的优先级,否则将它们放到一起运算肯定要出乱子,正如算术中有先乘除后加减的规则,C语言同样要有确定的运算规则。C语言定义了15个算符优先级,其优先级规则如下:

规则1:优先级高的先运算

规则2:同一个优先级则需要按照结合性确定运算顺序,大部分为从左到右,仅有少数结合性为从右到左。

1 C语言运算符的优先级和结合性列表

优先级 运算符 含义 结合性
1 ( )
[ ]
.
->
圆括号
数组下标 a[N]
结构体成员 对象.成员
结构体指针 对象指针->成员名
从左到右
2 !
~
++
--
-
(类型)
*
&
sizeof
逻辑非
按位取反
自增
自减
负号运算符
强制类型转换
取值, *p表示某指针的内容
取址, &a表示变量a的地址
长度运算
从右到左
3 *
/
%


求余
从左到右
4 +
-

从左到右
5 >>
<<
右移
左移
从左到右
6 <
>
<=
>=
小于
大于
小于等于
大于等于
从左到右
7 ==
!=
等于运算符
不等于运算符
从左到右
8 & 按位余 从左到右
9 ^ 按位异或 从左到右
10 | 按位或 从左到右
11 && 逻辑与 从左到右
12 || 逻辑或 从左到右
13 ? : 条件判断 从右到左
14 =
+=
-=
*=
/=
%=
&=
^=
|=
>>=
<<=
赋值运算符
加后赋值
减后赋值
乘后赋值
除后赋值
取余后赋值
按位与后赋值
按位异或后赋值
按位或后赋值
右移后赋值
左移后赋值
从右到左
15 , 逗号运算符 从左到右

2 C语言容易弄错的算符优先级

此表摘录《C专家编程》

优先级问题 表达式 错误的理解 正确的结果
.的优先级高于*
->操作符用于消除这个问题
*p.f p所指对象的字段 *(p).f 对p取f偏移,作为指针,然后进行解除引用操作*(p.f)
[] 高于* int *ap[] ap是个指向int数组的指针 int (*ap)[] 指针数组 int *(ap[])
==和!=高于位操作运算符 (val & mask != 0) (val & mask) != 0 val & (mask != 0)
==和!= 高于赋值运算符 c = getchar() != EOF (c = getchar()) != EOF c = (getchar() != EOF)
算术运算符高于移位运算符 msb << 4 + lsb (msb << 4) + lsb msb << (4 + lsb)
逗号运算符在所有运算符中优先级最低 i = 1, 2 i = (1, 2) (i = 1), 2

另外补充几个:

优先级问题 表达式 错误的理解 正确的结果
==的优先级高于&& 和 || a && b == c (a && b) == c a && (b == c)
->的优先级高于& &p->offset (&p)->offset &(p>offset)

小结:

完全记住这些优先级有点困难,并且没有必要,可以简单的记一些:

  1. 括号(圆括号,中括号),结构体运算符(. 与 ->)优先级最高
  2. 赋值,逗号运算符优先级最低
  3. 单目运算符(* & 等 )优先级排第2
  4. 算术>移位>关系(比较)> 按位与或>逻辑与或(逻辑非处于第二等级)>赋值
  5. 只有单目运算符,三目运算符,赋值运算符的结合性为从右到左,其它结合性都是从左到右

3 算符优先级有关的几个知识点

3.1 指针数组与数组指针

下标引用[ ]的优先级高于间接访问*

指针数组:int *p[5]

数组指针:int (*p)[5]

指针数组:

本质是数组,只不过是是指针数组,如下例多个指针:

int a = 1;
int b = 2;
int c = 3;
int d = 4;
int *pa = &a;
int *pb = &b;
int *pc = &c;
int *pd = &d;

printf("a = %d b = %d c = %d, d = %d\r\n", *pa, *pb, *pc, *pd);

/* 或者定义指针数组,*p[4]是一个指针数组,数组里面的每个元素都是一个指针 */
int *p[4] = {&a, &b, &c, &d};
for (int i = 0; i < 4; i++) {
    printf("%d ", *p[i]);
}
printf("\r\n");

结果为:

a = 1 b = 2 c = 3, d = 4
1 2 3 4

指针数组多用来存放字符串列表,便于管理

char *str[] = {
    "hello",
    "what's your name",
    "my name is hanmeimei",
    "how are you"
};
for (int i = 0; i < 4; i++) {
    printf("%s \r\n", str[i]); // 因为要打印字符串,所以这里用的是str[i],而非*str[i]
}

结果为:

hello
what's your name
my name is hanmeimei
how are you

数组指针:

本质是一个指针,这个指针指向数组,相当于行指针,一般用来指向二维数组。

int aa[6] = {1, 2, 3, 4, 5, 6};
int *pa = aa;
int (*p)[3] = &aa;

printf("aa = 0x%x, &aa = 0x%x, &aa[0] = 0x%x\r\n", aa, &aa, &aa[0]);
printf("p = 0x%x, *p = 0x%x *(p + 0) = 0x%x, **(p + 0) = 0x%x, p[0] = 0x%x, *p[0] = 0x%x, pa = 0x%x, *pa = 0x%x\r\n", p, *p, *(p + 0), **(p + 0), p[0], *p[0], pa, *pa);
p = p + 1;
pa = pa + 1;
printf("p = 0x%x, *p = 0x%x *(p + 0) = 0x%x, **(p + 0) = 0x%x, p[0] = 0x%x, *p[0] = 0x%x, pa = 0x%x, *pa = 0x%x\r\n", p, *p, *(p + 0), **(p + 0), p[0], *p[0], pa, *pa);

打印如下:

aa = 0x900fffa8, &aa = 0x900fffa8, &aa[0] = 0x900fffa8
p = 0x900fffa8, *p = 0x900fffa8 *(p + 0) = 0x900fffa8, **(p + 0) = 0x1, p[0] = 0x900fffa8, *p[0] = 0x1, pa = 0x900fffa8, *pa = 0x1
p = 0x900fffb4, *p = 0x900fffb4 *(p + 0) = 0x900fffb4, **(p + 0) = 0x4, p[0] = 0x900fffb4, *p[0] = 0x4, pa = 0x900fffac, *pa = 0x2

第一行打印说明:数组的地址与数组名称相同,数组的地址和数组首元素的地址数值上相同(但这并不意味着数组的地址和数组首元素的地址是完全相同的)

第二行打印说明:数组指针的解引用为行首元素地址,而数组元素指针的解引用为元素值。

第三行打印说明:数组指针指向的是数组的地址(不是元素的地址),指针加1相当于跳过当前数组,指向下一个数组。

数组指针和多维数组的关系:

数组 数组指针
二维 int arr[3][4] int (*p)[4]
三维 int arr[3][4][5] int (*p)[4][5]
四维 int arr[3][4][5][6] int (*p)[4][5][6]

如下用数组指针来遍历二维数组:

int aa[3][2] = {1, 2, 3, 4, 5, 6};
int (*p)[2] = aa;
for (int i = 0; i < 6; i++) {
   for (int j = 0; j < 2; j++) {
      printf("%d ", *(*(p + i) + j)); // *(p + i) 为第i行数组指针的解引用,表示第i行首元素地址,
                                      // 所以 *(p + i) + j) 表示第i行第j列元素地址
   }
   printf("\r\n");
}

输出日志为:

1 2
3 4
5 6

3.2 函数指针与指针函数

圆括号的优先级高于*

指针函数:int * fun(int x,int y);
函数指针:int (*fun)(int x,int y);

指针函数:

类似于 int * fun(int x,int y); 这种形式,本质是函数,函数的返回值为指针。

函数指针:

本质是指针变量,这个指针指向函数。

int (*fun)(int x,int y); // 定义

fun = &Function;         // 赋值,以下两种方式均可
fun = Function;

x = (*fun)();            // 调用,以下两种方式均可,建议用1,可以清楚的指明这是通过指针的方式来调用函数
x = fun();

3.2 *p++与(*p)++与*(p++)与*++p与++*p

*的优先级与++相同,这时候需要查看其结合性,* 与++算符的结合性是从右到左

所以:*p++与*(p++)等价

*p++与*(p++) 表示先取*p的值,然后p指针自增,等价于 res = *p; p++;

(*p)++ 表示取*p的值,然后*p的值加1,等价于:res = *p; (*p)++;

*++p 等价*(++p),表示p先自增,然后取这个指针位置的元素,等价于 ++p; res = *p

++*p等价于++(*p),表示*p的值加1, 然后取值,等价于:(*p) = *(p) + 1; res = *(p)

参考:

  1. C语言运算符优先级列表(超详细)

  2. C语言常被搞错的运算符优先级

  3. 彻底理解数组指针和指针数组的区别-CSDN博客

  4. 指针的各种用法

  5. 数组指针详解

  6. C语言:数组的地址和数组首元素的地址的区别

posted @ 2024-03-23 15:46  sureZ_ok  阅读(82)  评论(0编辑  收藏  举报