方括号[]优先级高于,但括号()最高;int p[5]中p先与[5]结合为5元指针数组,int (p)[5]中(p)先结合为指向含5个int数组的指针。

看声明里括号和方括号谁先“绑定”变量名
核心就一条:[] 优先级高于 *,但括号 () 优先级最高。所以读声明时,从变量名出发,先看紧挨着它的符号是谁。
比如 int *p[5]:p 先和 [5] 结合 → p 是一个有 5 个元素的数组;剩下 int * → 每个元素是 int* 类型。这就是指针数组。
而 int (*p)[5]:括号强制 *p 先结合 → 是一个指针;再看外面 <code>[5] → 这个指针指向的是一个含 5 个 int 的数组。这就是数组指针。
容易踩的坑:
- 误把
int *p[5]当成“指向 5 个 int 的指针”,其实它根本不是指针,是数组名,不能做p++ - 写
int (*p)[5] = arr;时,arr必须是真正类型为int[5]或更高维(如int[3][5])的数组,不能是int*或int** -
sizeof(p)在两者中结果天差地别:int *p[5]的sizeof(p)是5 * sizeof(int*);int (*p)[5]的sizeof(p)就是sizeof(int*)
用典型初始化方式一眼识别
初始化写法暴露本质。你写出来的第一行赋值,往往就决定了它是哪种类型。
指针数组常见初始化:
-
const char *names[] = {"Alice", "Bob", "Charlie"};—— 每个字符串字面量地址存进数组元素 -
int a=1, b=2, c=3; int *ptrs[] = {&a, &b, &c};—— 地址分散、内存不连续
数组指针常见初始化:
-
int arr[3][5]; int (*p)[5] = arr;—— 直接把二维数组首地址赋给指针 -
int vec[5] = {0}; int (*q)[5] = &vec;—— 注意是&vec,不是vec(后者退化为int*,类型不匹配)
关键区别:指针数组的初始化列表里是“一堆地址”,数组指针的初始化右边通常是“一个数组名或其地址”,且必须带维度信息。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
传参场景下类型不匹配会直接编译失败
C++ 函数参数里最容不得模糊。传错类型,编译器立刻报错,但错误信息可能让人更迷。
比如你要写一个处理字符串列表的函数:
- 正确用指针数组:
void print_strings(const char *strs[], int n)——strs是数组,每个元素是const char* - 若误写成
void print_strings(const char (*strs)[])...,编译器会报“expected ‘]’ before ‘)’”,因为(*strs)[]缺少长度,不合法
再比如处理固定列数的二维数组:
- 正确用数组指针:
void process_rows(int (*mat)[5], int rows)——mat[i][j]可直接访问第i行第j列 - 若传
int **mat进去,即使数据布局一样,也会编译失败:类型不兼容,int (*)[5]和int **完全不同
这里没有运行时妥协空间——C++ 的类型系统在函数签名层面就卡死,错一点就过不去。
sizeof 和指针运算行为完全不同
这是验证你是否真懂的“照妖镜”。光背定义没用,一算就露馅。
对 int *p[5]:
-
sizeof(p)返回5 * 8(64 位下),即整个数组占的空间 -
p + 1是地址加sizeof(int*)(8 字节),跳到下一个指针元素 -
p[2]是第三个指针,*p[2]才是它指向的 int 值
对 int (*p)[5]:
-
sizeof(p)就是8,它本身只是一个指针变量 -
p + 1是地址加5 * sizeof(int)(20 字节),跳过一整行 -
(*p)[2]或p[0][2]是它所指数组的第三个元素;p[1][2]是下一行第三个元素
最容易忽略的一点:数组指针的 p[i] 等价于 *(p + i),得到的是一个 int[5] 类型的左值,可以继续用 [j] 下标;而指针数组的 p[i] 是 int*,解引用一次才到值。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










