数组和指针的关系

发布于:2025-08-04 ⋅ 阅读:(17) ⋅ 点赞:(0)

在 C 语言中,​指针和数组有着非常紧密的联系,但它们本质上是 ​不同的概念。理解它们的关系是掌握 C 语言内存操作的关键。下面我会从多个角度帮你梳理 ​指针和数组的直接联系,并解释它们的异同点。

1. 数组和指针的本质区别

概念 本质 存储方式 能否修改指向 典型用途
数组(Array)​ 一组 ​连续存储的同类型数据 在栈或静态区分配固定大小的内存 ❌ 不能整体修改(数组名是常量指针) 存储固定数量的数据
指针(Pointer)​ 一个 ​变量,存储内存地址 可以指向任意内存位置(栈、堆、静态区) ✅ 可以修改指向(指向不同地址) 动态内存操作、函数传参

关键区别​:

  • 数组名 arr 在大多数情况下会退化为指向首元素的指针(&arr[0]),但它本身不是指针变量​(不能重新赋值,如 arr = &x 是错误的)。
  • 指针是一个变量,可以存储任意地址,并且可以修改指向​(如 int *p = &x; p = &y;)。

2. 数组和指针的直接联系

​(1) 数组名在大多数情况下退化为指针(指向首元素)​

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;  // 等价于 int *p = &arr[0];
  • ​**arr​ 的类型是 int[5](数组),但在表达式里(如赋值给指针时),它会 ​退化为 int*(指向 arr[0] 的指针)​**。
  • ​**arr 和 &arr[0] 是等价的**,都表示数组首元素的地址。

​(2) 数组名 arr 和 &arr 的区别

表达式 类型 含义
arr int*(退化) 指向 ​首元素 arr[0] 的指针
&arr int (*)[5] 指向 ​整个数组 arr 的指针​(类型是 int[5] 的指针)
arr + 1 移动 ​1 个 int 大小(4 字节)​ 指向 arr[1]
&arr + 1 移动 ​整个数组大小(5 * 4 = 20 字节)​ 指向 arr 的下一个数组(如果有的话)

示例​:

int arr[5] = {1, 2, 3, 4, 5};
printf("%p\n", arr);     // 数组首元素地址(等价于 &arr[0])
printf("%p\n", &arr);    // 整个数组的地址(值和 arr 相同,但类型不同)
printf("%p\n", arr + 1); // 指向 arr[1](地址 + 4 字节)
printf("%p\n", &arr + 1); // 指向 arr 的下一个位置(地址 + 20 字节)

输出​:

0x7ffd12345670  (arr)
0x7ffd12345670  (&arr,值相同)
0x7ffd12345674  (arr + 1,+4 字节)
0x7ffd12345684  (&arr + 1,+20 字节)

结论​:

  • arr 和 &arr 的 ​值相同​(都是数组的起始地址),但 ​类型不同​:
    • arr 是 int*(指向 int)。
    • &arr 是 int (*)[5](指向 int[5] 数组)。
  • arr + 1 和 &arr + 1 的 ​步长不同​:
    • arr + 1 移动 ​1 个 int 大小(4 字节)​
    • &arr + 1 移动 ​整个数组大小(5 * 4 = 20 字节)​

​(3) 数组访问方式 vs 指针访问方式

数组方式
int arr[3] = {10, 20, 30};
printf("%d\n", arr[1]);  // 20(数组下标访问)
指针方式
int *p = arr;
printf("%d\n", *(p + 1));  // 20(指针偏移访问)

等价关系​:

  • arr[i] 等价于 *(arr + i)
  • p[i] 等价于 *(p + i)

结论​:

  • 数组下标访问 arr[i] 底层就是指针偏移 *(arr + i)
  • 指针可以像数组一样使用 [] 运算符​(因为 [] 本质是指针算术)。

​(4) 函数传参时数组退化为指针

void printArray(int arr[], int size) {  // 实际上 arr 是 int*
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);  // 等价于 *(arr + i)
    }
}

int main() {
    int myArr[3] = {1, 2, 3};
    printArray(myArr, 3);  // 数组名退化为指针
    return 0;
}

关键点​:

  • ​**函数参数中的 int arr[] 实际上等价于 int *arr**​(编译器不会把数组完整传进去,而是传首地址)。
  • 所以 ​在函数内部无法通过 sizeof(arr) 获取数组大小​(只能得到指针大小,通常是 4 或 8 字节)。

3. 指针和数组的常见操作对比

操作 数组方式 指针方式
访问第 i 个元素 arr[i] *(p + i) 或 p[i]
遍历数组 for (int i = 0; i < n; i++) { arr[i]; } for (int *p = arr; p < arr + n; p++) { *p; }
函数传参 void func(int arr[])(实际是 int* void func(int *p)
获取首地址 arr 或 &arr[0] p(指针本身)
获取数组大小 sizeof(arr) / sizeof(arr[0])(仅限数组定义处) ❌ 无法直接获取(只能手动传大小)

4. 总结

​(1) 指针和数组的联系

✅ ​数组名在大多数情况下会退化为指向首元素的指针​(如 arr → &arr[0])。
✅ ​数组访问 arr[i] 底层就是指针算术 *(arr + i)
✅ ​指针可以像数组一样使用 [] 运算符​(如 p[i])。
✅ ​函数传参时,数组会退化为指针​(无法在函数内获取数组真实大小)。

​(2) 指针和数组的区别

❌ ​数组名不是指针变量​(不能重新赋值,如 arr = &x 是错误的)。
❌ ​**arr 和 &arr 类型不同​(arr 是 int*&arr 是 int (*)[n])。
❌ ​
数组在栈/静态区分配固定大小,指针可以指向任意内存(堆、栈、静态区)​**。

​(3) 关键结论

  • 数组名 arr 在大多数情况下可以当作指针使用,但它本质不是指针变量
  • 指针更灵活,可以指向任意内存,而数组名是固定的
  • 函数传参时,数组会退化为指针,所以无法在函数内获取数组真实大小​(必须额外传 size 参数)。


网站公告

今日签到

点亮在社区的每一天
去签到