3. 指针运算与数组遍历

任务目标

本案例要求使用指针算术完成对整型数组的遍历与求和,并与下标访问方式在代码层面进行对比。重点在于理解指针算术的移动规则(p + n等价于&arr[n]),以及两种遍历方式在嵌入式环境中的实际差异。

核心实现

#include <stdio.h>
#include <stdint.h>

/* 数组求和:下标访问版本 */
int32_t sum_by_index(const int32_t *arr, uint32_t len) {
    int32_t sum = 0;
    for (uint32_t i = 0; i < len; i++) {
        sum += arr[i];
    }
    return sum;
}

/* 数组求和:指针算术版本 */
int32_t sum_by_pointer(const int32_t *arr, uint32_t len) {
    int32_t sum = 0;
    const int32_t *p = arr;
    const int32_t *end = arr + len;   /* 指向末尾之后 */
    while (p < end) {
        sum += *p;
        p++;                          /* 每次移动 sizeof(int32_t) 字节 */
    }
    return sum;
}

/* 指针算术遍历:打印每个元素地址偏移 */
void print_offsets(const int32_t *arr, uint32_t len) {
    const int32_t *p = arr;
    for (uint32_t i = 0; i < len; i++) {
        printf("arr[%u] 地址偏移: %ld 字节, 值: %d\n",
               i, (long)(p - arr) * (long)sizeof(int32_t), *p);
        p++;
    }
}

int main(void) {
    int32_t test_arr[] = {10, 20, 30, 40, 50};
    uint32_t n = sizeof(test_arr) / sizeof(test_arr[0]);

    int32_t s1 = sum_by_index(test_arr, n);
    int32_t s2 = sum_by_pointer(test_arr, n);

    printf("下标求和: %d\n", s1);
    printf("指针求和: %d\n", s2);
    printf("结果一致: %s\n", (s1 == s2) ? "是" : "否");

    print_offsets(test_arr, n);
    return 0;
}

编译与运行

# 使用 gcc 编译(嵌入式交叉编译器同理)
gcc -Wall -O2 -o ptr_traverse ptr_traverse.c

# 运行
./ptr_traverse

验证方法

  • 功能正确性:程序输出中“下标求和”与“指针求和”均为150,且“结果一致”显示“是”。
  • 指针移动验证:print_offsets输出的地址偏移应为0、4、8、12、16字节(在32位int系统上),证明p++按sizeof(int32_t)步进。
  • 效率对比说明:在-O2优化下,两种方式生成的机器码通常相同。若需观察差异,可改用-O0编译并对比执行时间——但本案例不要求实测,仅从代码层面理解:指针版本通过p++直接修改地址,省去了下标版本中每次计算base + i * scale的乘法操作(未优化时)。

关键点说明

  • 边界指针:end = arr + len指向数组最后一个元素之后,是合法的指针值,但不可解引用。循环条件p < end保证不越界。
  • 指针减法:p - arr返回元素个数差,乘以sizeof(int32_t)得到字节偏移,用于验证移动距离。
  • const限定:遍历函数参数使用const int32_t *,表明只读访问,防止意外修改数组内容。
arr[0] arr[1] arr[2] arr[3] arr[4] end p++ 移动4字节 循环条件: p < end p 初始指向 arr[0] 不可解引用