4. 指针与数组的关系

任务目标

在嵌入式C开发中,数组名与指针常被混用,但二者本质不同。本案例通过一个最小可运行程序,演示如何使用指针遍历数组,并明确数组名与指针在类型、可修改性和sizeof运算上的差异。

核心代码

#include <stdio.h>

int main(void) {
    int arr[5] = {10, 20, 30, 40, 50};
    int *p = arr;          // 数组名arr隐式转换为指向首元素的指针

    // 1. 用指针遍历数组
    printf("指针遍历结果: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", *(p + i));   // p+i 指向第i个元素,*解引用取值
    }
    printf("\n");

    // 2. 数组名与指针的异同对比
    printf("arr[2] = %d, *(p+2) = %d\n", arr[2], *(p + 2));  // 下标与指针等价

    // 数组名是常量地址,不能自增;指针是变量,可以自增
    p++;                          // 合法:p指向arr[1]
    printf("p自增后指向: %d\n", *p);

    // sizeof差异:数组名代表整个数组,指针只代表指针本身
    printf("sizeof(arr) = %zu (整个数组字节数)\n", sizeof(arr));
    printf("sizeof(p)   = %zu (指针变量字节数)\n", sizeof(p));

    return 0;
}

编译与运行

# 使用gcc编译(嵌入式交叉编译器用法相同)
gcc -o arr_ptr_demo arr_ptr_demo.c

# 运行
./arr_ptr_demo

验证方法与判定标准

  • 指针遍历正确性:程序输出 10 20 30 40 50,说明通过 *(p+i) 成功访问每个元素。
  • 下标等价性:arr[2] 与 *(p+2) 均输出 30,验证 a[i] 本质是 *(a+i) 的语法糖。
  • 可修改性差异:p++ 编译通过并输出 20;若尝试 arr++ 则编译报错,因为数组名是常量地址。
  • sizeof差异:在32位系统上 sizeof(arr) 输出 20(5个int×4字节),sizeof(p) 输出 4(指针本身大小)。在64位系统上后者为 8。该差异直接证明数组名不是指针变量。

关键说明

数组名在表达式中会隐式转换为指向首元素的指针(例外:作为sizeof、&操作数时),但数组名本身是地址常量,不能作为左值赋值。指针变量则保存地址且可重新指向。在嵌入式场景中,理解此差异可避免误用sizeof计算数组长度(如sizeof(arr)/sizeof(arr[0])只能用于真实数组,不能用于已退化为指针的函数参数)。