4 一维数组与二维数组

正在加载视频播放器…

GESP C++ 四级:一维数组与二维数组系统梳理

核心目标:深入掌握一维数组的定义、初始化、遍历与常见算法,系统掌握二维数组的定义、初始化、遍历、矩阵操作与函数传参,能熟练解决矩阵转置、边界处理、行列统计等综合问题。
一维数组是“一排连续的同类型存储单元”,二维数组是“一维数组的数组”;下标从0开始,遍历用嵌套循环,矩阵操作注意行列关系和边界条件。

一、考纲定位

GESP 四级考纲对数组的要求包括:

  • 掌握一维数组的基本概念及使用,能熟练进行遍历、求和、求最值、查找等操作。
  • 掌握二维及多维数组的基本概念及使用,理解二维数组的行列结构,能进行矩阵的遍历、转置、统计等操作。
  • 理解数组作为函数参数的传递方式(数组名退化为指针)。
  • 四级不考动态内存分配,二维数组的列数必须在编译期确定。

二、一维数组核心回顾

2.1 定义与初始化

int arr[10];                   // 定义,未初始化
int arr2[5] = {1, 2, 3, 4, 5}; // 完全初始化
int arr3[5] = {1, 2};          // 部分初始化,其余补0
int arr4[] = {1, 2, 3};        // 自动推导长度
int arr5[100] = {0};           // 全部清零(最常用)

关键规则:

  • 数组长度必须是编译期常量,不能用变量定义(四级仍不考变长数组)。
  • 下标从 0 开始,长度为 n 的数组有效下标是 0 ~ n-1。
  • 数组越界不会产生编译错误,但会导致未定义行为。

2.2 遍历与常见操作

// 标准遍历
for (int i = 0; i < n; i++) { ... }

// 逆序遍历
for (int i = n - 1; i >= 0; i--) { ... }

// 求和
int sum = 0;
for (int i = 0; i < n; i++) sum += arr[i];

// 求最值
int maxv = arr[0];
for (int i = 1; i < n; i++) if (arr[i] > maxv) maxv = arr[i];

// 查找
int pos = -1;
for (int i = 0; i < n; i++) {
    if (arr[i] == target) { pos = i; break; }
}

// 逆序
for (int i = 0, j = n - 1; i < j; i++, j--) {
    swap(arr[i], arr[j]);
}

2.3 数组与指针(四级重点)

数组名是首地址,是常量指针,不能重新赋值。

int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;          // p 指向 arr[0]
cout << *(p + 2);      // 30,等价于 arr[2]
cout << p[3];          // 40,等价于 arr[3]

数组作函数参数时退化为指针,函数内 sizeof(arr) 得到的是指针大小,不是数组总大小:

void f(int arr[]) {
    cout << sizeof(arr);   // 8(64位系统),不是数组总字节数
}

int main() {
    int a[10];
    cout << sizeof(a);     // 40
    f(a);                  // 输出 8
}

结论:数组作为参数时,必须额外传递长度 n。

三、二维数组核心语法

3.1 定义与内存布局

数据类型 数组名[行数][列数];
int matrix[3][4];       // 3行4列
double scores[5][3];    // 5行3列
char grid[10][10];      // 10行10列

内存布局:二维数组在内存中是按行连续存储的。matrix[3][4] 相当于 3 个长度为 4 的一维数组依次排列。

内存布局(按行连续):
matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],
matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3],
matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3]

3.2 初始化

// 方式一:分行初始化(推荐,清晰直观)
int a[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

// 方式二:连续初始化(按行填充)
int b[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

// 方式三:部分初始化,其余补0
int c[3][4] = {{1}, {2}, {3}};   // 每行只初始化第一个元素

// 方式四:全部清零
int d[3][4] = {0};

// 方式五:省略行数,列数必须写
int e[][4] = {{1,2,3,4}, {5,6,7,8}};   // 自动推导为2行
int f[][4] = {1,2,3,4,5,6,7,8};        // 自动推导为2行

关键规则:定义二维数组时,列数必须写,行数可以省略(由初始化列表推导)。

3.3 遍历

标准双重循环:

int n = 3, m = 4;
for (int i = 0; i < n; i++) {          // 外层控制行
    for (int j = 0; j < m; j++) {      // 内层控制列
        cout << a[i][j] << " ";
    }
    cout << endl;                       // 每行结束换行
}

按列遍历:

for (int j = 0; j < m; j++) {
    for (int i = 0; i < n; i++) {
        cout << a[i][j] << " ";
    }
    cout << endl;
}

3.4 二维数组与函数参数

二维数组作为函数参数时,列数必须在编译期可知。

// 方式一:省略首维长度(最常见)
void printMatrix(int a[][4], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            cout << a[i][j] << " ";
        }
        cout << endl;
    }
}

// 方式二:固定两维
void printMatrix(int a[3][4]) { ... }

// 方式三:指针+手算下标
void printMatrix(int *a, int rows, int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            cout << a[i * cols + j] << " ";
        }
        cout << endl;
    }
}

真题示例:以下哪个函数声明在调用时可以传递二维数组的名字作为参数?
答案:void BubbleSort(int a[3][4]);

四、常见矩阵操作

4.1 矩阵转置

将矩阵的行列互换:b[j][i] = a[i][j]。

int a[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int b[4][3];

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        b[j][i] = a[i][j];
    }
}
// b 变为 4行3列

4.2 矩阵加法

int c[3][4];
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        c[i][j] = a[i][j] + b[i][j];
    }
}

4.3 矩阵乘法

// a: n×m, b: m×p, c: n×p
int c[3][4] = {0};
for (int i = 0; i < n; i++) {
    for (int j = 0; j < p; j++) {
        for (int k = 0; k < m; k++) {
            c[i][j] += a[i][k] * b[k][j];
        }
    }
}

4.4 对角线元素

// 主对角线(左上到右下)
for (int i = 0; i < n; i++) {
    cout << a[i][i] << " ";
}

// 副对角线(右上到左下)
for (int i = 0; i < n; i++) {
    cout << a[i][n - 1 - i] << " ";
}

4.5 矩阵边界处理

// 判断是否在边界
bool isEdge(int i, int j, int n, int m) {
    return i == 0 || i == n - 1 || j == 0 || j == m - 1;
}

4.6 行列统计

// 每行之和
for (int i = 0; i < n; i++) {
    int rowSum = 0;
    for (int j = 0; j < m; j++) {
        rowSum += a[i][j];
    }
    cout << "第" << i << "行之和:" << rowSum << endl;
}

// 每列之和
for (int j = 0; j < m; j++) {
    int colSum = 0;
    for (int i = 0; i < n; i++) {
        colSum += a[i][j];
    }
    cout << "第" << j << "列之和:" << colSum << endl;
}

五、一维数组与二维数组的对比

对比项 一维数组 二维数组
定义 int arr[10]; int arr[3][4];
内存布局 连续一排 按行连续
遍历 单层循环 双重循环
函数参数 int arr[] + 长度 int arr[][列数] + 行数
常见操作 求和、最值、查找、逆序 转置、加法、乘法、对角线
典型应用 数列处理、统计 矩阵运算、图像处理、棋盘

六、GESP四级数组相关真题示例

真题1:二维数组参数声明

以下哪个函数声明在调用时可以传递二维数组的名字作为参数?
A) void BubbleSort(int a[][], int n, int m);
B) void BubbleSort(int a[3][4]);
C) void BubbleSort(int *a, int n);
D) void BubbleSort(int a[][], int n);
答案:B
解析:二维数组作参数时,列数必须在编译期确定,所以 int a[3][4] 是正确的。

真题2:数组名与指针

以下代码的输出是( )。

int a[3] = {1, 2, 3};
int *p = a;
cout << *(p + 2);

答案:3
解析:*(p + 2) 等价于 a[2],值为 3。

真题3:数组作函数参数

以下程序输出是( )。

void f(int arr[]) {
 cout << sizeof(arr) << endl;
}
int main() {
 int a[10];
 f(a);
}

答案:8(64位系统,指针大小)
解析:数组作参数退化为指针,sizeof(arr) 得到指针大小。

真题4:指针与数组地址计算

假设整数数组 arr[4] = {0, 1, 2, 3}; 的第一个元素在内存中的地址为 0x7ffee4065820,经过 int* p = arr; p += 1; 后,指针 p 的值是( )。
答案:0x7ffee4065824(假设 int 占 4 字节)

七、易错点汇总

7.1 一维数组易错点

  1. 数组越界:访问 arr[n] 或负下标,不会编译错误,但会导致未定义行为。
  2. 数组名不能赋值:arr = p; 错误,数组名是常量。
  3. 数组长度必须是常量:int n; int arr[n]; 不合规。
  4. 函数内求数组长度错误:数组参数退化为指针,必须额外传长度。
  5. 最值初值取0:数组全为负数时结果错误,应取 arr[0] 或足够小的数。

7.2 二维数组易错点

  1. 列数必须写:int a[][] 错误,int a[][4] 正确。
  2. 行列混淆:a[i][j] 中 i 是行,j 是列,遍历时外层行内层列。
  3. 函数参数列数必须编译期可知:void f(int a[][], int m) 错误。
  4. 内存布局理解错误:二维数组按行连续,a[i][j] 的地址是 基地址 + (i * 列数 + j) * sizeof(类型)。
  5. 对角线条件错误:主对角线 i == j,副对角线 i + j == n - 1(n为阶数)。
  6. 矩阵乘法维度不匹配:n×m 矩阵乘以 m×p 矩阵,结果是 n×p。

八、速查表

需求 写法 说明
定义一维数组 int arr[10]; 长度必须常量
一维数组清零 int arr[10] = {0}; 全部为0
一维遍历 for (i=0; i<n; i++) 下标0~n-1
定义二维数组 int a[3][4]; 3行4列
二维数组清零 int a[3][4] = {0}; 全部为0
二维遍历 外层行,内层列 i行j列
二维数组作参数 void f(int a[][4], int rows) 列数必须写
矩阵转置 b[j][i] = a[i][j]; 行列互换
主对角线 a[i][i] 左上到右下
副对角线 a[i][n-1-i] 右上到左下
行和 固定i,累加j 每行求和
列和 固定j,累加i 每列求和
矩阵乘法 三重循环 c[i][j] += a[i][k]*b[k][j]

九、总结

一维数组与二维数组是GESP四级核心数据结构。

  1. 一维数组是连续的同类型存储单元,下标从0开始。
  2. 二维数组是“一维数组的数组”,内存按行连续存储。
  3. 定义二维数组时,列数必须写,行数可省略。
  4. 遍历二维数组用双重循环:外层控制行,内层控制列。
  5. 二维数组作函数参数时,列数必须在编译期可知。
  6. 数组名是首地址,作参数时退化为指针,必须额外传长度。
  7. 矩阵转置:b[j][i] = a[i][j]。
  8. 主对角线 i == j,副对角线 i + j == n - 1(n为阶数)。
  9. 数组越界不会编译错误,但会导致未定义行为,务必检查边界。
  10. 多练“矩阵转置”“行列统计”“数组作参数”三类题。

复习建议:先把一维数组的求和、最值、查找、逆序四个模板写熟,再系统掌握二维数组的定义、初始化、遍历、矩阵操作。重点练习“二维数组作函数参数”和“矩阵转置”两类题。考试中先确定数组维度,再设计循环结构,最后检查边界和输出格式。