C 语言实例 – 使用指针访问数组元素(快速上手)

C 语言实例 – 使用指针访问数组元素

在学习 C 语言的过程中,指针与数组的关系常常让初学者感到困惑。很多人说:“指针是 C 语言的灵魂。” 这句话不假。而当你真正理解了“如何用指针访问数组元素”时,你会发现自己对内存操作、函数传参、动态内存管理等高级主题的理解也变得更深刻。

本文将通过一系列清晰、实用的 C 语言实例,带你一步步掌握“使用指针访问数组元素”这一核心技能。我们不讲抽象概念,而是从代码出发,结合实际场景,手把手带你写出可运行、可理解、可复用的代码。


指针与数组的亲密关系

在 C 语言中,数组名本质上是一个指向数组首元素的指针常量。这个观点虽然听起来抽象,但它是理解后续所有操作的基础。

想象一下:你有一个书架,上面整齐地摆着 5 本《C 语言入门》。书架的名字叫 books,而第一本书的位置就是 books[0]。如果你拿着一个标签写着“第一本书的位置”,那么这个标签其实就是指向第一个元素的指针。

在代码中,这样的关系是:

int numbers[5] = {10, 20, 30, 40, 50};
int *ptr = numbers;  // ptr 指向 numbers[0]

这里 numbers 是数组名,它等价于 &numbers[0],也就是第一个元素的地址。所以 ptr 被赋值为 numbers 后,就拥有了访问整个数组的能力。


创建数组与初始化

我们先从一个最基础的示例开始。创建一个整型数组,并用指针来遍历它。

#include <stdio.h>

int main() {
    // 定义一个包含 5 个整数的数组
    int arr[5] = {100, 200, 300, 400, 500};

    // 声明一个指向 int 类型的指针
    int *p;

    // 将指针指向数组首元素(arr[0] 的地址)
    p = arr;

    // 通过指针访问每个元素
    for (int i = 0; i < 5; i++) {
        printf("第 %d 个元素的值是: %d\n", i + 1, *(p + i));
        // *(p + i) 等价于 arr[i]
    }

    return 0;
}

代码注释说明:

  • int arr[5] = {100, 200, 300, 400, 500};:创建一个长度为 5 的整型数组,并初始化。
  • int *p;:声明一个指针变量 p,它能指向一个整型变量的地址。
  • p = arr;:将数组名 arr 赋值给指针 p。由于数组名代表首地址,所以 p 现在指向 arr[0]
  • *(p + i):使用指针算术。p + i 是第 i 个元素的地址,* 取出该地址所存储的值。
  • printf 输出:我们用 i + 1 使编号从 1 开始,更符合人类阅读习惯。

运行结果如下:

第 1 个元素的值是: 100
第 2 个元素的值是: 200
第 3 个元素的值是: 300
第 4 个元素的值是: 400
第 5 个元素的值是: 500

指针算术:地址的“步进”机制

指针支持加减运算,这正是它能高效遍历数组的关键。在 C 中,p + 1 并不是加 1 个字节,而是加一个 int 类型所占的字节数(通常是 4 字节)。

我们用一个例子来展示这一点:

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40};
    int *p = arr;  // p 指向 arr[0]

    // 打印每个元素及其地址
    for (int i = 0; i < 4; i++) {
        printf("arr[%d] = %d, 地址: %p\n", i, *(p + i), p + i);
    }

    return 0;
}

输出示例:

arr[0] = 10, 地址: 0x7ffee5f1a9b0
arr[1] = 20, 地址: 0x7ffee5f1a9b4
arr[2] = 30, 地址: 0x7ffee5f1a9b8
arr[3] = 40, 地址: 0x7ffee5f1a9bc

关键点解析:

  • p + i 每次增加的步长是 sizeof(int),即 4 字节。
  • 地址连续递增,说明指针在内存中“跳跃”地访问数据。
  • %p 是打印指针地址的标准格式。

这个机制让指针访问数组比下标访问更高效,尤其是在处理大型数组或复杂数据结构时。


指针与数组下标的等价性

在 C 语言中,arr[i]*(arr + i) 是完全等价的。这是 C 语言设计的精妙之处。

我们来验证这一点:

#include <stdio.h>

int main() {
    int data[] = {5, 15, 25, 35, 45};
    int *ptr = data;

    // 使用下标访问
    printf("下标方式: data[2] = %d\n", data[2]);

    // 使用指针算术访问
    printf("指针方式: *(ptr + 2) = %d\n", *(ptr + 2));

    // 两者结果相同
    if (data[2] == *(ptr + 2)) {
        printf("两者结果一致!\n");
    }

    return 0;
}

输出:

下标方式: data[2] = 25
指针方式: *(ptr + 2) = 25
两者结果一致!

原理:

  • data[i] 被编译器翻译为 *(data + i)
  • 指针访问是底层实现,下标访问是语法糖。
  • 两者效率几乎相同,但指针方式在某些场景(如函数参数传递)中更灵活。

实际应用场景:函数中使用指针处理数组

在真实项目中,我们经常需要将数组作为参数传入函数。此时,使用指针传递数组是标准做法。

#include <stdio.h>

// 函数声明:接收一个整型数组指针和数组长度
void printArray(int *arr, int size) {
    printf("数组元素为:");
    for (int i = 0; i < size; i++) {
        // 通过指针访问元素
        printf("%d ", *(arr + i));
    }
    printf("\n");
}

int main() {
    int numbers[] = {100, 200, 300, 400};
    int n = 4;  // 数组长度

    // 调用函数,传入数组名(即首地址)
    printArray(numbers, n);

    return 0;
}

输出:

数组元素为:100 200 300 400 

为什么传数组名而不是整个数组?

  • 数组名作为参数时,会退化为指针。
  • 传地址比复制整个数组更高效,尤其对于大数组。
  • 函数内部通过指针修改数组内容,原数组也会被修改(传址调用)。

指针与字符串的结合使用

字符串在 C 语言中本质上是字符数组,以 \0 结尾。使用指针处理字符串,是常见的编程实践。

#include <stdio.h>

int main() {
    char message[] = "Hello, C!";
    char *str = message;  // str 指向字符串首字符

    printf("字符串内容:");
    while (*str != '\0') {
        printf("%c", *str);
        str++;  // 指针后移,访问下一个字符
    }
    printf("\n");

    return 0;
}

输出:

字符串内容:Hello, C!

关键点:

  • *str 取当前字符,str++ 移动指针。
  • 循环直到遇到字符串结束符 \0
  • 这种方式在字符串处理(如 strlenstrcpy)中极为常见。

总结与建议

通过这一系列 C 语言实例,我们深入理解了“使用指针访问数组元素”的核心思想。从基础的指针赋值,到指针算术,再到函数传参和字符串处理,每一步都体现了指针在内存管理中的强大能力。

记住几个关键点:

  • 数组名等价于首地址,可以赋给指针。
  • *(p + i)p[i] 完全等价。
  • 指针算术按数据类型大小“步进”。
  • 用指针传递数组更高效、更灵活。

对于初学者,建议多动手写代码,尝试将下标访问改写为指针访问,反向操作也练习。当你能熟练使用指针遍历数组时,你就真正掌握了 C 语言的“底层思维”。

C 语言实例 – 使用指针访问数组元素,不仅是语法练习,更是编程能力的跃迁。坚持练习,你会在未来的项目中感受到它的价值。