C/C++定义指针与数组时的优先级问题

指针与数组

首先记住:括号的优先级大于星号。因此:

1
2
3
4
5
int* p[10]; // []优先与p结合,所以p是一个长度为10的数组,元素是int指针
*p[2] = 10; // 先获取数组下标为2的指针p[2],再对它解引用,把10写入p[2]指向的那块内存

int (*p) [10]; // 小括号内星号与p结合,所以p是一个指针,指向的是长度为10的int数组
*p[2] = 10;

另一个典型的例子是,main函数中常见的传参写法:

1
char* argv[]; // []优先与argv结合,所以这是二级指针char** argv

类似地,对于函数指针,规则也一样适用:

1
2
int* f();  // 括号先与f结合,所以f是一个函数,返回值是int指针
int (*f) (); // 括号内星号与f结合,所以f是一个函数指针,这个函数的返回值是int

第二条规则,中括号和小括号优先级一样。他们都是从左向右求值

1
int (*f[10])(); // f是一个数组,数组的元素是指针,指针指向返回值是int的函数

函数指针

注意区分函数函数指针

1
2
3
4
5
6
7
8
9
10
11
12
typedef int funcType(int x); // funcType是一个函数类型,
funcType f1; // f1的声明(declaration)。它们都是funcType这个类型的函数

int f1(int x) { // f1的定义(definition)
//......
}
funcType f2 = f1; // 报错。不能把一个函数赋值给另一个函数。

typedef int (*funcPtr)(int x); // funcPtr是一个函数指针
funcPtr p1 = &f1;
funcPtr p1 = f1; // 和上一行效果一样,但不建议用,容易混淆
(*p1)(3); // 调用f1

不要混淆func&func的用法,虽然很多时候两者通用。

现在可以丧心病狂一点,把函数、指针和数组结合起来。但是注意,不能建立函数的数组,只能建立函数指针的数组

1
2
3
int f[10](); // 报错。不能建立函数的数组。
int (*f)[10](); // 报错。同上。
int f()[10]; // 报错。函数不能返回数组。

挑战一下,吴总给的两个例子:

1
2
3
// 注意搞清楚,哪些是定义的标识符,哪些仅仅是参数名
char *(* x[10])(int **y);
int (*(*(*z)(int *))[5])(int *);

数组指针的运算

记住指针运算的规则:指针加一,地址增加一个元素的大小。得到的指针类型不变。

1
2
3
4
5
6
7
int(*p)[10]; // p是指向int[10]的指针

// p+1跳过的是一个int[10]的距离。p+1的类型和p相同,都是int(*)[10]
p + 1;

// p[1]等价于*(p+1)。由于解引用,得到的类型是int[10],即一维数组。
p[1];

对于指向定长数组的指针,分配内存的方式如下:

1
int(*p)[10] = new int[XXX][10];

与const的结合

我没有查到标准里关于const的规则。网上大多的说法是:const先左结合,如果左边没有了就右结合

先看一级指针。这两种写法是等价的:

1
2
3
4
5
6
7
8
int const* p;
const int* p;
p = another_ptr; // 合法,p本身可变
*p = 66; // 错误!p指向的值不可变

int *const p = ptr; // const左结合,修饰星号,那么p是常指针
p = NULL; // 错误!p本身不可变
*p = 66; // OK

再看二级指针。

1
2
3
4
5
const int **p; // p[i][j]不可变
int const **p; // 与上一行等价

int *const *p; // p[i]不可变
int **const p; // p本身不可变

再加上数组。

1
2
3
char const *argv[]; // array of pointer to const char
const char *argv[]; // 同上
char *const argv[]; // array of const pointer to char

cdecl

cdecl: C gibberish ↔ English

这个网站可以将诸如int *p(int (*)[10])之类的复杂定义转换成英语。

目前已经支持指针数组、函数指针、const。不过不支持typedef,不支持多个标识符名,功能还是有点鸡肋。

题外话:数组到指针的转换

以前上课学过,在函数传参的时候,一维数组可以转换为一级指针。

这里可以理解为,int[N]可以转换为int*类型,而int(*)[N]则不可以。因为前者指向的是单个元素,而后者指向的是N个元素组成的一块。

1
2
3
4
5
6
int a[10];

int* p1 = a; // OK
int(*p2)[10] = a; // cannot convert ‘int*’ to ‘int (*)[10]’
int(*p3)[10] = &a; // OK
int(*p4)[20] = &a; // cannot convert ‘int (*)[10]’ to ‘int (*)[20]’

实际上,数组作为函数参数传递后会退化会指针。

1
2
3
4
void func(int arr[10]) {
cout << sizeof(arr) << endl;
// 输出指针本身的大小,而且编译器会抛警告
}

参考文章

CppReference - new expression

c - What does a typedef with parenthesis like “typedef int (f)(void)” mean? Is it a function prototype? - Stack Overflow

c - Typedef function and is it useful? - Stack Overflow

c - correct way to assign function pointer - Stack Overflow

Declaring an array of functions of type void C++ - Stack Overflow

c++ - What is the difference between const int*, const int* const, and int const* ? - Stack Overflow

c++ - Const before or const after? - Stack Overflow


C/C++定义指针与数组时的优先级问题
https://yalandhong.github.io/2022/12/29/cpp/pointer_and_array/
作者
Yaland Hong
发布于
2022年12月29日
许可协议