指针数组与一维数组指针有何区别?

99ANYc3cd6
预计阅读时长 22 分钟
位置: 首页 C语言 正文

核心概念速览

概念 英文名 定义/声明 核心特点 内存模型
一维数组 One-dimensional Array int arr[5]; 一块连续的内存空间,存放多个相同类型的元素。 arr[0] arr[1] arr[2] arr[3] arr[4] (连续)
数组指针 Pointer to an Array int (*p)[5]; 一个指针,它指向一个完整的数组。 p -> [arr[0] arr[1] arr[2] arr[3] arr[4]] (指向整个数组)
指针数组 Array of Pointers int *p[5]; 一个数组,它的每个元素都是一个指针 p[0] p[1] p[2] p[3] p[4] (每个元素都是一个指针地址)

一维数组

这是最基础的概念。

c语言 一维数组指针数组指针
(图片来源网络,侵删)

定义

一维数组是用于存储相同类型元素的一系列连续内存位置。

声明

数据类型 数组名[元素个数];

int arr[5]; // 定义一个包含5个整型元素的数组,名为arr

关键点

  1. 连续内存:系统会为数组分配一块连续的内存空间。arr[0] 的地址最低,arr[4] 的地址最高。
  2. 数组名 arr 的双重身份
    • 代表整个数组:在大多数情况下(如 sizeof(arr)&arr),arr 代表整个数组。
    • 代表首元素地址:在表达式(如 arr + 1 或作为函数参数传递时),arr 会“退化”为其首元素 arr[0] 的地址。

示例与内存图

#include <stdio.h>
int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    // 1. sizeof(arr) 计算的是整个数组的字节大小
    printf("sizeof(arr) = %zu\n", sizeof(arr)); // 输出 20 (5 * 4)
    // 2. &arr 获取的是整个数组的地址
    printf("&arr = %p\n", (void*)&arr);
    // 3. arr 在表达式中,代表首元素 arr[0] 的地址
    printf("arr = %p\n", (void*)arr);
    printf("&arr[0] = %p\n", (void*)&arr[0]);
    // 4. arr + 1 地址跳过的是整个数组的大小 (sizeof(int) * 5)
    printf("arr + 1 = %p\n", (void*)(arr + 1));
    printf("&arr[1] = %p\n", (void*)&arr[1]); // 与上者不同!
    return 0;
}

内存图解:

      内存地址 (递增)
  +---------------------+
  |  0x7ffd...  | 10   |  <-- arr[0], *arr
  +---------------------+
  |  0x7ffd...  | 20   |  <-- arr[1]
  +---------------------+
  |  0x7ffd...  | 30   |  <-- arr[2]
  +---------------------+
  |  0x7ffd...  | 40   |  <-- arr[3]
  +---------------------+
  |  0x7ffd...  | 50   |  <-- arr[4]
  +---------------------+
       ^
       |
       |--- arr (首元素地址)
       |
       |--- &arr (整个数组的地址)

数组指针

定义

数组指针是一个指针变量,它指向一个完整的数组,而不是单个元素。

c语言 一维数组指针数组指针
(图片来源网络,侵删)

声明

数据类型 (*指针名)[元素个数]; 注意括号 ! 它是必须的,因为它将 和指针名 p 绑定在一起,表示 p 是一个指针,如果没有括号,int *p[5] 就成了“指针数组”。

int (*p)[5]; // p 是一个指针,它指向一个包含5个int元素的数组

关键点

  1. 指向一个整体p 指向的不是 arr[0],而是整个 arr 数组。
  2. 指针运算的跨度p + 1 时,指针的值会加上 sizeof(整个数组),也就是 sizeof(int) * 5,这与 arr + 1 的行为一致。

示例与内存图

#include <stdio.h>
int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int (*p)[5] = &arr; // p 指向整个 arr 数组
    printf("p = %p\n", (void*)p);
    printf("&arr = %p\n", (void*)&arr);
    printf("arr = %p\n", (void*)arr); // arr 和 &arr 的值可能相同,但类型和含义不同
    // 解引用 p 得到的是它指向的整个数组
    // 在表达式中,这个数组会“退化”为首元素的地址
    printf("*p = %p\n", (void*)*p); // *p 等价于 arr
    printf("**p = %d\n", **p);       // **p 等价于 *arr,即 arr[0] 的值 10
    // p + 1 跳过整个数组
    printf("p + 1 = %p\n", (void*)(p + 1));
    // 遍历数组 (用法不常见,但能说明问题)
    for (int i = 0; i < 5; i++) {
        printf("(*p)[%d] = %d\n", i, (*p)[i]); // (*p) arr
    }
    return 0;
}

内存图解:

      内存地址 (递增)
  +---------------------+
  |  0x7ffd...  | 10   |  <-- arr[0]
  +---------------------+
  |  0x7ffd...  | 20   |  <-- arr[1]
  +---------------------+
  |  0x7ffd...  | 30   |  <-- arr[2]
  +---------------------+
  |  0x7ffd...  | 40   |  <-- arr[3]
  +---------------------+
  |  0x7ffd...  | 50   |  <-- arr[4]
  +---------------------+
       ^
       |
       |--- &arr (整个数组的地址)
       |
       |--- p (p 的值是 &arr 的值)

指针数组

定义

指针数组是一个数组,它的所有元素都是指针变量

声明

数据类型 *指针名[元素个数]; 注意没有括号 ! 由于 [] 的优先级高于 ,编译器会先解析 p[5],认为这是一个数组, 表示这个数组的元素是指针。

int *p[5]; // p 是一个包含5个元素的数组,每个元素都是一个指向 int 的指针

关键点

  1. 一个数组,装满了指针:它本身是一个数组,不是指针。
  2. 元素的类型:每个元素 p[0], p[1], ... 都是一个指针,可以用来存储其他变量的地址。
  3. 最常见的用途:用于存储多个字符串(字符数组的首地址)。

示例与内存图

#include <stdio.h>
int main() {
    int a = 10, b = 20, c = 30, d = 40, e = 50;
    int *p[5]; // 定义一个指针数组
    // 让每个指针元素指向一个整型变量
    p[0] = &a;
    p[1] = &b;
    p[2] = &c;
    p[3] = &d;
    p[4] = &e;
    // sizeof(p) 计算的是整个指针数组的字节大小
    printf("sizeof(p) = %zu\n", sizeof(p)); // 输出 40 (5 * 8,在64位系统上)
    // 遍历指针数组,通过指针间接访问其指向的值
    for (int i = 0; i < 5; i++) {
        printf("*p[%d] = %d\n", i, *p[i]);
    }
    // p[i] 等价于 *(p + i)
    printf("*(p + 2) = %d\n", *(p + 2)); // 输出 c 的值 30
    return 0;
}

内存图解:

         栈内存
  +-------------------+  <-- p (数组首地址)
  |  0x... (a的地址)  |  <-- p[0]
  +-------------------+
  |  0x... (b的地址)  |  <-- p[1]
  +-------------------+
  |  0x... (c的地址)  |  <-- p[2]
  +-------------------+
  |  0x... (d的地址)  |  <-- p[3]
  +-------------------+
  |  0x... (e的地址)  |  <-- p[4]
  +-------------------+
                      |
                      v
  +-------------------+  <-- a 的内存
  |       10          |
  +-------------------+
  +-------------------+  <-- b 的内存
  |       20          |
  +-------------------+
  ... 以此类推 ...

总结与对比

特性 一维数组 int arr[5] 数组指针 int (*p)[5] 指针数组 int *p[5]
本质 一块连续的内存空间 一个指针变量 一个数组
指向 存放数据元素 指向一个完整的数组 存放多个指针
sizeof sizeof(类型) * 元素个数 sizeof(指针) (通常是 8 字节) sizeof(指针) * 元素个数
p + 1 跳过 sizeof(类型) 个字节 跳过 sizeof(类型) * 元素个数 个字节 跳过 sizeof(指针) 个字节
典型用途 存储同类型数据集合 处理二维数组(作为行指针) 存储字符串数组或一组变量的地址
记忆口诀 数据的集合 指向数组的指针 指针的集合

何时使用?

  • 一维数组:当你需要一组固定数量的、同类型的数据时,例如存储一个班级的5个学生的成绩。
  • 数组指针
    • 主要用途:处理二维数组,二维数组在内存中是连续存储的,每一行都可以看作一个一维数组,数组指针(行指针)可以方便地遍历二维数组的每一行。
      int matrix[3][4];
      int (*p)[4] = matrix; // p 指向 matrix 的第一行
      p++; // p 指向 matrix 的第二行
    • 作为函数参数,表示函数期望接收一个二维数组。
  • 指针数组
    • 主要用途:管理多个字符串,C语言中字符串通常用字符数组表示,指针数组可以方便地管理这些字符串数组的首地址,实现类似“字符串列表”的功能。
      char *names[] = {"Alice", "Bob", "Charlie"};
      // names[0] 指向 "Alice" 的首字符 'A'
    • 当你需要一组指针来指向不同的变量时。

希望这个详细的解释能帮助你彻底理解这三个概念!关键在于区分“什么是指针”和“什么是数组”,以及它们是如何组合在一起的。

-- 展开阅读全文 --
头像
c语言 main 函数的位置
« 上一篇 02-18
手机端网址多m是什么原因?
下一篇 » 02-18

相关文章

取消
微信二维码
支付宝二维码

目录[+]