2026-02-21 · 约 7 分钟 · C 语言

C 语言指针与数组名:几道让我想明白的练习题

C 语言基础

指针和数组这块一直是我的薄弱点,平时写代码靠惯性,一旦题目换个花样就拿不准。 最近找了几道小练习,把每道题的输出自己推一遍再上机验证, 有几个一直模糊的地方终于清楚了,记录一下。

一、数组名到底是不是指针

常听到「数组名就是指针」,这句话不准确。看这个例子:

#include <stdio.h>

int main(void) {
    int arr[5] = {1, 2, 3, 4, 5};

    printf("sizeof(arr)      = %zu\n", sizeof(arr));
    printf("sizeof(&arr)     = %zu\n", sizeof(&arr));
    printf("sizeof(arr + 0)  = %zu\n", sizeof(arr + 0));

    return 0;
}

在 64 位机器上的输出:

sizeof(arr)      = 20
sizeof(&arr)     = 20
sizeof(arr + 0)  = 8

arr 是数组类型的首地址常量,sizeof 得到的是整个数组的字节数 (5 个 int,20 字节)。而 arr + 0 参与运算时,数组名就退化成了 int *,所以是 8 字节(指针大小)。

&arr 的类型是 int (*)[5],指向整个数组的指针, sizeof 得到 20 是因为它解引用后是整个数组。这几个结果要能各自解释清楚才算过关。

二、&arr + 1 跳多远

int arr[5] = {1, 2, 3, 4, 5};
int *p1 = (int *)(&arr + 1);
int *p2 = arr + 5;

printf("p1 - arr = %ld\n", p1 - arr);
printf("p2 - arr = %ld\n", p2 - arr);

输出是:

p1 - arr = 5
p2 - arr = 5

结果虽然相同,原因不一样。&arr 的步长是整个数组, &arr + 1 一步就跳过了 5 个 int;arr + 5 是 int * 按单个 int 步进,走了 5 步。一个是跳了一个数组,一个是跳了五个元素。

三、二维数组怎么传参

二维数组传参时,形参的第一维可以省略,第二维不行:

void print(int a[][3], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }
}

// 也可以写成指针形式
void print2(int (*a)[3], int rows) {
    /* 内部用法完全一样 */
}

原因是内存里二维数组是按行连续存放的,知道一行有几个元素, 才能算出 a[i][j] 的偏移量 i * 3 + j。第二维缺失就无法定位, 所以编译器要求写明。

四、一个容易混的写法

int *p[3];    // 指针数组:3 个 int* 组成的数组
int (*p)[3];  // 数组指针:指向 int[3] 的一个指针

记法很简单:看 p 先跟谁结合。p[3] 是下标运算,优先级高于 *,所以前者是数组;加了括号就先跟 * 结合,是指针。

小结

  • 数组名是首地址常量,参与运算才退化成指针,sizeof 结果因此不同。
  • &arr + 1 的步长是整个数组,arr + 1 的步长是单个元素。
  • 二维数组传参必须给出第二维,因为要靠它计算地址偏移。
  • 指针数组和数组指针,看 p 先跟谁结合。
← 上一篇 下一篇 →