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 *,表明只读访问,防止意外修改数组内容。