深入理解指向数组的指针以及寻址运算

深入理解指向数组的指针以及寻址运算

文章目录

在C语言的学习中,指针和数组是两个核心概念,而它们之间的关系更是初学者的一大难点。尤其是"指向数组的指针"和"指针的寻址运算",理解透彻了,才能真正掌握C语言的精髓。今天我们就来系统地探讨这个话题,并辅以丰富的代码示例,帮你彻底搞懂。


一、为什么需要指向数组的指针?

数组是内存中一块连续的空间,而指针则用来存储内存地址。通过指针操作数组,可以实现:

  • 更灵活的数组访问(如动态遍历)
  • 高效地传递数组给函数
  • 实现动态多维数组
  • 理解底层内存布局

二、基础概念回顾

1. 数组名是什么?

在大多数表达式中,数组名会被转换为指向其第一个元素的指针。例如:

c 复制代码
int arr[5] = {1,2,3,4,5};
int *p = arr;  // arr 等价于 &arr[0]

2. 指针运算

指针加1,实际增加的字节数等于指针所指类型的字节数。即:

复制代码
p + i  = p + i * sizeof(类型)

三、一维数组的指针与寻址

示例1:通过指针遍历一维数组

c 复制代码
#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int *p = arr;  // 指向第一个元素

    printf("数组首地址: %p\n", arr);
    printf("p 的值: %p\n", p);
    printf("&arr[0]: %p\n\n", &arr[0]);

    // 指针遍历
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d, *(p+%d) = %d, 地址: %p\n",
               i, arr[i], i, *(p + i), p + i);
    }

    // 指针移动演示
    printf("\n初始 *p = %d\n", *p);
    p++;          // 移动到下一个元素
    printf("p++ 后 *p = %d\n", *p);
    p += 2;       // 向后移动两个元素
    printf("p+=2 后 *p = %d\n", *p);

    return 0;
}

输出说明
p + i 的地址每次增加4个字节(假设 int 占4字节),且 *(p + i)arr[i] 等价。


四、二维数组的指针与寻址

二维数组本质上是"数组的数组"。例如 int matrix[3][4] 是一个包含3个元素的数组,每个元素又是一个包含4个int的数组。

1. 行指针(指向一维数组的指针)

要指向整个一行(即一个一维数组),需要使用行指针:

c 复制代码
int (*p)[4] = matrix;  // p 指向包含4个int的一维数组

这里的 p 就是一个指向数组的指针,每次 p+1 会跳过一行(4个int)。

示例2:通过行指针访问二维数组

c 复制代码
#include <stdio.h>

int main() {
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9,10,11,12}
    };
    int (*p)[4] = matrix;  // 行指针

    printf("matrix: %p\n", matrix);
    printf("matrix[0]: %p\n", matrix[0]);
    printf("&matrix[0][0]: %p\n\n", &matrix[0][0]);

    // 通过行指针访问元素
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            // 三种等价写法
            printf("matrix[%d][%d] = %d  ", i, j, matrix[i][j]);
            printf("= *(*(p+%d)+%d) = %d  ", i, j, *(*(p + i) + j));
            printf("地址: %p\n", *(p + i) + j);
        }
        printf("\n");
    }

    // 行指针的步长
    printf("p 的地址: %p\n", p);
    printf("p+1 的地址: %p\n", p + 1);
    printf("一行大小: %ld 字节\n", sizeof(int) * 4);

    return 0;
}

关键点

  • p + i 指向第 i 行的首地址。
  • *(p + i) 得到第 i 行的首元素地址(相当于 matrix[i]&matrix[i][0])。
  • *(p + i) + j 得到第 i 行第 j 列元素的地址。
  • *(*(p + i) + j) 取出该元素的值。

2. 另一种理解:二级指针?

很多人会把二维数组名赋值给 int **,这是错误的。因为 matrix 不是指针的指针,而是连续的二维内存块。int ** 适用于指针数组,后面会讲。


五、数组指针 vs 指针数组

这两个概念极易混淆,我们来区分一下:

类型 定义 含义
数组指针 int (*p)[n]; p 是一个指针,指向一个包含 n 个 int 的数组
指针数组 int *p[n]; p 是一个数组,包含 n 个指向 int 的指针

示例3:指针数组

c 复制代码
#include <stdio.h>

int main() {
    int a = 1, b = 2, c = 3;
    int *pArr[3] = {&a, &b, &c};  // 指针数组

    for (int i = 0; i < 3; i++) {
        printf("pArr[%d] 指向的值: %d, 地址: %p\n", i, *pArr[i], pArr[i]);
    }

    // 二维数组 vs 指针数组的内存布局完全不同
    return 0;
}

指针数组的每个元素独立指向一个 int,内存不连续;而二维数组是连续存储的。


六、实际应用场景

1. 函数参数传递

当需要将二维数组传递给函数时,必须指明第二维的大小,或者使用数组指针:

c 复制代码
void printMatrix(int rows, int cols, int (*mat)[cols]) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", mat[i][j]);  // 或 *(*(mat+i)+j)
        }
        printf("\n");
    }
}
// 调用:printMatrix(3, 4, matrix);

2. 动态分配多维数组

利用数组指针可以方便地分配动态二维数组:

c 复制代码
int (*p)[4] = malloc(3 * sizeof(*p));  // 分配 3 行,每行 4 个 int
// 使用 p[i][j] 访问
free(p);

七、常见陷阱与注意事项

  1. 不要把二维数组名直接赋给 int **,会导致编译错误或运行时崩溃。

  2. 数组指针的括号不能少int (*p)[4]int *p[4] 天差地别。

  3. 指针加减运算的单位是指针所指类型的大小,理解这一点才能正确寻址。

  4. arr&arr 的区别arr 是第一个元素的地址,&arr 是整个数组的地址,数值相同但步长不同。

    c 复制代码
    int arr[5];
    printf("%p %p\n", arr, arr+1);   // 相差 4 字节
    printf("%p %p\n", &arr, &arr+1); // 相差 20 字节

八、总结

指向数组的指针是C语言中强大而灵活的工具,它让我们能够以指针的方式操作数组,特别是在多维数组和动态内存管理中不可或缺。理解寻址运算的关键在于:

  • 明确指针的类型,知道每次移动的字节数。
  • 分清"数组指针"和"指针数组"。
  • 掌握 *(*(p+i)+j) 的层层递进关系。

希望通过本文的讲解和代码示例,你能对指向数组的指针以及寻址运算有更深刻的理解。多动手写代码,多调试观察内存地址,才能真正融会贯通。

欢迎在评论区留言讨论,分享你的学习心得或疑问!

相关推荐
星火开发设计1 小时前
序列式容器:list 双向链表的特性与用法
开发语言·前端·数据结构·数据库·c++·链表·list
洛_尘2 小时前
测试6:自动化测试--概念篇(JAVA)
java·开发语言·测试
wjs20242 小时前
Lua 字符串处理详解
开发语言
『往事』&白驹过隙;2 小时前
在ARM开发中 volatile与const关键字的关键用途
c语言·arm开发·mcu·物联网·学习·iot
航哥的女人2 小时前
最小可运行示例(C++ TCP回显)
开发语言·c++·tcp/ip
lsx2024063 小时前
React 事件处理
开发语言
JQLvopkk4 小时前
能用C#开发AI
开发语言·人工智能·c#
郝学胜-神的一滴5 小时前
当AI遇见架构:Vibe Coding时代的设计模式复兴
开发语言·数据结构·人工智能·算法·设计模式·架构
阿里嘎多学长9 小时前
2026-02-16 GitHub 热点项目精选
开发语言·程序员·github·代码托管