06 数组名、指针数组与数组指针

一、核心结论:指针 +1 的步长

指针执行 +1,移动一个“所指向类型”的大小,而不是固定增加 1 字节。

int a[3] = {10, 20, 30};
int *p = a;         // 指向一个 int
int (*q)[3] = &a;   // 指向整个 int[3] 数组
表达式类型+1 步长(假设 int 为 4 字节)
a(通常表达式中)int *4 字节
&a[0]int *4 字节
&aint (*)[3]12 字节

相同起始地址,不同步长

地址      0x1000      0x1004      0x1008      0x100C
          ┌───────────┬───────────┬───────────┐
数组 a    │ a[0] = 10 │ a[1] = 20 │ a[2] = 30 │
          └───────────┴───────────┴───────────┘
          ↑           ↑                       ↑
          a           a+1                     &a+1
          &a
  • a + 1:跨一个 int,指向 a[1]。
  • &a + 1:跨一个 int[3],指向整个数组的尾后位置,不可解引用。
  • a 本身是数组对象,在大多数表达式中才转换为首元素指针;&a 指向整个数组。

二、指针数组 vs 数组指针(新增)

对比指针数组数组指针
声明int *p[3]int (*p)[3]
本质数组指针
含义存放 3 个 int *指向 int[3] 数组
常见访问*p[1](*p)[1]
自身能否重新赋值数组不能整体赋值指针可以重新赋值

1. 指针数组:int *p[3]

        ┌──────┬──────┬──────┐
        │ p[0] │ p[1] │ p[2] │
        └──┬───┴──┬───┴──┬───┘
           ↓      ↓      ↓
         x=10   y=20   z=30
int x = 10, y = 20, z = 30;
int *p[3] = {&x, &y, &z};
printf("%d\n", *p[1]);  // 20

p[1] 是保存 y 地址的指针;*p[1] 读取 y 的值。

2. 数组指针:int (*p)[3]

              ┌────┬────┬────┐
       p ───→ │ 10 │ 20 │ 30 │
              └────┴────┴────┘
                    数组 a
int a[3] = {10, 20, 30};
int (*p)[3] = &a;
printf("%d\n", (*p)[1]); // 20

p 指向整个数组;*p 是数组本身;(*p)[1] 读取其第 2 个元素。

三、*p[1] 与 (*p)[1]

表达式运算顺序
*p[1]先取 p[1],后解引用
(*p)[1]先解引用 p,再取下标 1
#include <stdio.h>

int main(void)
{
    int x = 10, y = 20;
    int *ptrs[2] = {&x, &y};

    int a[3] = {30, 40, 50};
    int (*row)[3] = &a;

    printf("%d %d\n", *ptrs[1], (*row)[1]);
    return 0;
}

输出:20 40。

无括号时,[] 比一元 * 优先级高;括号可以改变分组。

四、修改值 vs 修改指向

int a[2] = {10, 20};
int b[2] = {30, 40};
int *ptrs[2] = {&a[0], &b[0]};
int (*row)[2] = &a;

*ptrs[0] = 50;  // 修改 a[0]
row = &b;       // 让 row 指向数组 b
数组 a:[50] [20]     数组 b:[30] [40]

ptrs[0] ─→ a[0] = 50
ptrs[1] ─→ b[0] = 30
row     ─→ 数组 b
表达式值
a[0]50
*ptrs[1]30
(*row)[1]40

修改一个指针的指向不会自动修改其他独立指针。

五、蓝牙/TWS 工程选用

需求推荐声明原因
保存多个独立对象的地址Type *ptrs[N]每个元素可指向不同对象
操作固定列数的二维数组Type (*row)[N]保留整行数组的类型和步长

六、快速记忆

  1. 指针 +1 按所指向类型大小移动。
  2. a + 1 跨一个元素;&a + 1 跨整个数组。
  3. int *p[3]:指针数组,数组里有三个指针。
  4. int (*p)[3]:数组指针,一个指针指向完整数组。
  5. *p[1]:先下标再解引用;(*p)[1]:先解引用再下标。
  6. 修改数据与改变指针指向不是一回事。

口诀:看括号辨类型,看指向类型算步长。