C语言中的几个难懂的指针概念讨论(附运算符优先级)
因为现在都用C#、Java一类的类型安全的语言了,关于最基本的C语言中和指针相关的几个概念,每次看懂之后,过一段总是又忘了……这次记下来:
一、关于 int* p 和 int *p 的区别:
上述两种形式在使用上没区别,当使用 int* p,q 和 int *p,q 的时候才会体现出区别。(牢记这一点有利于理解下文的内容,我个人习惯前一种写法,比较方便理解。)
注意:这两种形式不涉及间址运算符*在结合性上的优先级(完全由space决定)。
二、涉及数组 int *p[n] 和 int (*p)[n] 等:
1. int *p[n]
由于 [] 优先级比 * 高,因此 p 先与 [] 结合,将其理解为 int* p[n] 更容易,即 p 是一维数组名,数组 p[n] 的元素类型为 int*(指向int对象的指针)。举一例:
[code language="csharp"]<br />int a=b=c=0;
int *p[3]={ &a, &b, &c };<br />[/code]
2. int (*p)[n]
由于p先与*结合,声明p为一指针,其指向一个int[n],即具有n个int元素的一维数组。
数组名的实质是指针,因此指针p也可以当做二维数组名处理,等效于int p[][n]。考虑以下代码:
[code language="csharp"]
int a[2][3] = { {1,2,3}, {4,5,6} };
int (*p)[3];
p = a;
[/code]
注意到p+1指向二维数组的下一行,即{4,5,6},可以这样定义一个指针来指向一个二维数组。这里p与a不同的是:p是指针,可以移动(如p++),但是a是数组名,相当于符号常量,不能移动。
3. 数据类型 int(*)[n] 和 int*[n]
int(*)[n]为上述第2种定义中p的数据类型,先说明其是一个指针,指向 int[n];同理,可以有 int*[n] 类型,显然定义了一个数组(名),数组元素为int*,即上述第1中定义中p的数据类型。
从这里可以看出,这两种数据类型本质上是一致的,均为指针类型,所指对象是一个指向int元素的指针。因此上述定义1和定义2从本质上讲也是一致的,其区别类似于指针类型和数组名的区别(如能否移动等)。
三、函数指针 int *p() 与 int (*p)() 等:
1. int *p() 与上述类似,p先与()结合,可以理解为int* p(),即p是函数名,返回值是int*类型。
2. int (*p)()中,p是函数指针,指向一个返回值类型是int的函数,所指函数形参不确定。
3. int f()[] 试图声明一个返回值为数组int[]的函数f()。由于数组不能够作为函数返回值,声明不合法!
4. int f[]() 试图声明一维数组f[],数组成员是返回值为int的函数。该声明不合法,因为函数(不是函数指针)不能作为数组元素。函数(体)具有不同size,怎么能构成数组呢?
5. int (*f[])() 是合法声明,f先与[]结合,数组f[]中的元素是函数指针,所指函数的返回值为int。
附:C语言运算符优先级
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
|
() |
圆括号 |
(表达式)/函数名(形参表) |
|||
. |
成员选择(对象) |
对象.成员名 |
|||
-> |
成员选择(指针) |
对象指针->成员名 |
|||
2 |
- |
负号运算符 |
-表达式 |
右到左 |
单目运算符 |
(类型) |
强制类型转换 |
(数据类型)表达式 |
|||
++ |
自增运算符 |
++变量名/变量名++ |
单目运算符 |
||
-- |
自减运算符 |
--变量名/变量名-- |
单目运算符 |
||
* |
取值运算符 |
*指针变量 |
单目运算符 |
||
& |
取地址运算符 |
&变量名 |
单目运算符 |
||
! |
逻辑非运算符 |
!表达式 |
单目运算符 |
||
~ |
按位取反运算符 |
~表达式 |
单目运算符 |
||
sizeof |
长度运算符 |
sizeof(表达式) |
|||
3 |
/ |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
* |
乘 |
表达式*表达式 |
双目运算符 |
||
% |
余数(取模) |
整型表达式/整型表达式 |
双目运算符 |
||
4 |
+ |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
- |
减 |
表达式-表达式 |
双目运算符 |
||
5 |
<< |
左移 |
变量<<表达式 |
左到右 |
双目运算符 |
>> |
右移 |
变量>>表达式 |
双目运算符 |
||
6 |
> |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
>= |
大于等于 |
表达式>=表达式 |
双目运算符 |
||
< |
小于 |
表达式<表达式 |
双目运算符 |
||
<= |
小于等于 |
表达式<=表达式 |
双目运算符 |
||
7 |
== |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
!= |
不等于 |
表达式!= 表达式 |
双目运算符 |
||
8 |
& |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
9 |
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
10 |
| |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
11 |
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
12 |
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
13 |
?: |
条件运算符 |
表达式1? 表达式2: 表达式3 |
右到左 |
三目运算符 |
14 |
= |
赋值运算符 |
变量=表达式 |
右到左 |
|
/= |
除后赋值 |
变量/=表达式 |
|||
*= |
乘后赋值 |
变量*=表达式 |
|||
%= |
取模后赋值 |
变量%=表达式 |
|||
+= |
加后赋值 |
变量+=表达式 |
|||
-= |
减后赋值 |
变量-=表达式 |
|||
<<= |
左移后赋值 |
变量<<=表达式 |
|||
>>= |
右移后赋值 |
变量>>=表达式 |
|||
&= |
按位与后赋值 |
变量&=表达式 |
|||
^= |
按位异或后赋值 |
变量^=表达式 |
|||
|= |
按位或后赋值 |
变量|=表达式 |
|||
15 |
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
从左向右顺序运算 |
C语言运算符的“优先级口诀”
括号成员第一; //括号运算符[]() 成员运算符. ->
全体单目第二; //所有的单目运算符比如++ -- +(正) -(负) 指针运算*&
乘除余三,加减四; //这个"余"是指取余运算即%
移位五,关系六; //移位运算符:<< >> ,关系:> < >= <= 等
等于(与)不等排第七; //即== !=
位与异或和位或; //这几个都是位运算: 位与(&)异或(^)位或(|)
"三分天下"八九十;
逻辑或跟与; //逻辑运算符:|| 和 &&
十二和十一; //注意顺序:优先级(||) 底于 优先级(&&)
条件高于赋值, //三目运算符优先级排到 13 位只比赋值运算符和","高//需要注意的是赋值运算符很多!
逗号运算级最低! //逗号运算符优先级最低
本文固定链接: https://blog.xieyc.com/pointer-and-operator-precedence-in-c-programming-language/ | 小谢的小站