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字节)。

该输出直接由代码逻辑唯一确定,可通过观察确认行指针每次偏移跨越整行、列指针每次偏移仅跨越一个元素,从而验证两者的本质区别。

二维数组 int arr[3][4] 的内存布局与指针偏移 arr[0][0] arr[0][1] arr[0][2] arr[0][3] arr[1][0] arr[1][1] arr[1][2] arr[1][3] arr[2][0] arr[2][1] arr[2][2] arr[2][3] row_p + 1 偏移16字节(整行) col_p + 1 偏移4字节(单元素) 图例 第0行 第1行 第2行

图中可见:二维数组在内存中按行连续存储。行指针row_p的类型是int(*)[4],+1跳过一整行(4个int);列指针col_p的类型是int*,+1仅跳过1个int。两者指向同一内存起始地址,但指针算术步长不同,这正是行指针与列指针的核心区别。