017. 理解指针与数组的关系

在C语言中,指针和数组之间有着非常紧密的关系。理解它们之间的关系对于掌握C语言的高级特性非常重要。以下将详细解释指针与数组的关系,并通过具体示例帮助你更好地理解。

1. 数组的本质

在C语言中,数组本质上是一块连续的内存区域,用于存储多个相同类型的元素。数组的名称实际上是一个指向数组首元素的指针。

示例代码

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5}; // 声明并初始化一个数组
    int *ptr = arr; // 将指针初始化为数组的首地址

    printf("Address of arr: %p\n", arr); // 输出数组的首地址
    printf("Address of arr[0]: %p\n", &arr[0]); // 输出数组第一个元素的地址
    printf("Address stored in ptr: %p\n", ptr); // 输出指针存储的地址

    return 0;
}

输出结果

Address of arr: 0x7ffd5b9a5a44
Address of arr[0]: 0x7ffd5b9a5a44
Address stored in ptr: 0x7ffd5b9a5a44

解释

  • arr 是数组的名称,它表示数组的首地址。

  • &arr[0] 是数组第一个元素的地址。

  • ptr 是一个指针,初始化为数组的首地址。

  • 从输出结果可以看出,arr&arr[0]ptr 都指向同一个地址。

2. 通过指针访问数组元素

可以使用指针来访问数组的元素。通过指针加偏移量的方式,可以访问数组中的任意元素。

示例代码

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5}; // 声明并初始化一个数组
    int *ptr = arr; // 将指针初始化为数组的首地址

    printf("Element at index 0: %d\n", *ptr); // 通过指针访问第一个元素
    printf("Element at index 1: %d\n", *(ptr + 1)); // 通过指针访问第二个元素
    printf("Element at index 2: %d\n", *(ptr + 2)); // 通过指针访问第三个元素

    return 0;
}

输出结果

Element at index 0: 1
Element at index 1: 2
Element at index 2: 3

解释

  • *ptr 表示指针所指向的值,即数组的第一个元素。

  • *(ptr + 1) 表示指针加1后所指向的值,即数组的第二个元素。

  • *(ptr + 2) 表示指针加2后所指向的值,即数组的第三个元素。

3. 数组名作为函数参数

当数组作为函数参数时,传递的是数组的首地址。因此,函数内部可以通过指针操作来访问和修改数组的元素。

示例代码

#include <stdio.h>

// 函数定义:通过指针修改数组元素
void incrementArray(int *arr, int size) {
    for (int i = 0; i < size; i++) {
        arr[i]++; // 通过指针修改数组元素
    }
}

int main() {
    int arr[] = {1, 2, 3, 4, 5}; // 声明并初始化一个数组
    int size = sizeof(arr) / sizeof(arr[0]); // 计算数组的大小

    printf("Before incrementing:\n");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    // 调用函数并传递数组
    incrementArray(arr, size);

    printf("After incrementing:\n");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

输出结果

Before incrementing:
1 2 3 4 5 
After incrementing:
2 3 4 5 6 

解释

  • incrementArray函数中,arr是一个指向数组首地址的指针。

  • 通过arr[i]可以访问和修改数组的元素。

4. 指针与数组的等价性

在大多数情况下,数组名可以被看作是指向数组首元素的指针。以下是一些等价的表达式:

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

  • &arr[i] 等价于 arr + i

示例代码

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5}; // 声明并初始化一个数组

    printf("Element at index 2 (arr[2]): %d\n", arr[2]); // 通过数组访问
    printf("Element at index 2 (*(arr + 2)): %d\n", *(arr + 2)); // 通过指针访问

    printf("Address of element at index 2 (&arr[2]): %p\n", &arr[2]); // 通过数组获取地址
    printf("Address of element at index 2 (arr + 2): %p\n", arr + 2); // 通过指针获取地址

    return 0;
}

输出结果

Element at index 2 (arr[2]): 3
Element at index 2 (*(arr + 2)): 3
Address of element at index 2 (&arr[2]): 0x7ffd5b9a5a50
Address of element at index 2 (arr + 2): 0x7ffd5b9a5a50

5. 二维数组与指针

对于二维数组,数组名是一个指向数组第一行的指针。可以通过指针操作来访问和修改二维数组的元素。

示例代码

#include <stdio.h>

int main() {
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    }; // 声明并初始化一个二维数组

    int (*ptr)[4] = matrix; // 声明一个指向二维数组的指针

    printf("Element at [1][2] (matrix[1][2]): %d\n", matrix[1][2]); // 通过数组访问
    printf("Element at [1][2] (*(*(ptr + 1) + 2)): %d\n", *(*(ptr + 1) + 2)); // 通过指针访问

    return 0;
}

输出结果

Element at [1][2] (matrix[1][2]): 7
Element at [1][2] (*(*(ptr + 1) + 2)): 7

解释

  • matrix 是一个二维数组的名称,它是一个指向数组第一行的指针。

  • ptr 是一个指向二维数组的指针,可以通过*(*(ptr + 1) + 2)来访问二维数组的元素。

通过上述示例,你可以看到指针与数组之间的紧密关系:

  1. 数组名是一个指针:数组名表示数组的首地址。
  2. 通过指针访问数组元素:可以使用指针加偏移量的方式访问数组元素。
  3. 数组作为函数参数:传递的是数组的首地址,函数内部可以通过指针操作数组。
  4. 指针与数组的等价性arr[i] 等价于 *(arr + i)&arr[i] 等价于 arr + i
  5. 二维数组与指针:二维数组的名称是一个指向数组第一行的指针。

视频讲解

BiliBili: 视睿网络-哔哩哔哩视频 (bilibili.com)