4. 指针与多维数组
任务目标
编写一个C程序,使用行指针和列指针两种方式遍历并操作一个3×4的二维数组,通过对比两种指针的算术运算差异,理解行指针(指向整个一维数组的指针)与列指针(指向单个元素的指针)的本质区别。
核心实现
#include <stdio.h>
#define ROWS 3
#define COLS 4
// 使用行指针遍历并修改数组:每行元素乘以2
void process_by_row(int (*row_ptr)[COLS], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < COLS; j++) {
row_ptr[i][j] *= 2; // 等价于 *(*(row_ptr + i) + j)
}
}
}
// 使用列指针遍历并打印数组:每行元素加1
void process_by_column(int *col_ptr, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
// col_ptr 指向数组首元素,按线性地址偏移访问
*(col_ptr + i * cols + j) += 1;
}
}
}
int main(void) {
int arr[ROWS][COLS] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
printf("原始数组:\n");
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
printf("%3d ", arr[i][j]);
}
printf("\n");
}
// 行指针:指向第一行(长度为COLS的数组)
int (*row_p)[COLS] = arr;
process_by_row(row_p, ROWS);
printf("\n行指针处理(×2)后:\n");
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
printf("%3d ", arr[i][j]);
}
printf("\n");
}
// 列指针:指向首元素,按线性内存布局访问
int *col_p = &arr[0][0];
process_by_column(col_p, ROWS, COLS);
printf("\n列指针处理(+1)后:\n");
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
printf("%3d ", arr[i][j]);
}
printf("\n");
}
// 演示指针算术差异
printf("\n指针算术对比:\n");
printf("row_p + 1 跨越 %d 字节(整行)\n", (int)((char *)(row_p + 1) - (char *)row_p));
printf("col_p + 1 跨越 %d 字节(单元素)\n", (int)((char *)(col_p + 1) - (char *)col_p));
return 0;
}
编译与运行
gcc -Wall -o ptr_2d ptr_2d.c
./ptr_2d
验证方法与判定标准
程序输出应满足以下条件,即可判定实现正确:
- “原始数组”输出为1~12的3×4矩阵;
- “行指针处理(×2)后”每个元素是原始值的2倍(2,4,6,…,24);
- “列指针处理(+1)后”每个元素在上一步基础上加1(3,5,7,…,25);
- “指针算术对比”中,
row_p + 1的字节差应为4 * sizeof(int)(即16字节,若int为4字节),而col_p + 1的字节差应为sizeof(int)(即4字节)。
该输出直接由代码逻辑唯一确定,可通过观察确认行指针每次偏移跨越整行、列指针每次偏移仅跨越一个元素,从而验证两者的本质区别。
图中可见:二维数组在内存中按行连续存储。行指针row_p的类型是int(*)[4],+1跳过一整行(4个int);列指针col_p的类型是int*,+1仅跳过1个int。两者指向同一内存起始地址,但指针算术步长不同,这正是行指针与列指针的核心区别。