C++二维数组与数组指针详解:地址运算、定义规则及函数传参

Luna
Luna
发布时间:2026-10-16| 5人浏览过
原创

本文详细解析C++中二维数组的内存模型,解释二维数组名代表首行地址的原因,说明数组指针的定义方法与行列初始化约束。通过代码实例对比指针数组与二维数组的本质区别,并演示两种主流的二位数组函数传参技巧,帮助开发者消除概念混淆。

1、二维数组的内存地址特性

理解二维数组 int arr[3][3] 的核心在于区分“地址值”与“步长”。虽然 arr、arr[0] 和 &arr[0][0] 在数值上相等,但它们的类型含义不同:

  • arr 是二维数组的地址(指向首行),其步长为整行的字节数(例如3个int)。
  • arr[0] 是第一行的名称,等价于首元素地址,步长为单个元素的字节数(1个int)。

以下代码用于验证这种地址递增的差异:

#include <stdio.h>
#define ROW 3
#define COLUMN 3
int main()
{
    int arr[ROW][COLUMN] = {0};
    printf("arr = %p\n", arr);
    printf("arr[0] = %p\n", arr[0]);
    printf("&arr[0][0] = %p\n", &arr[0][0]);
    printf("\n\n");
    
    printf("arr + 1 = %p\n", arr + 1);     // 跳过一整行 (3*4 bytes)
    printf("arr[0] + 1 = %p\n", arr[0] + 1); // 跳过一个元素 (1*4 bytes)
    printf("&arr[0][0] + 1 = %p\n", &arr[0][0] + 1);
    printf("&arr[0][1]  = %p\n", &arr[0][1]);
    return 0;
}

运行结果清晰显示:arr + 1 的偏移量远大于 arr[0] + 1。这证明了 arr 确实是首行(一维数组)的地址,而非首元素 arr[0][0] 的地址。

第1步:正确定义数组指针保存二维数组

由于 arr 本质上是包含5个int的一维数组的地址,定义数组指针时必须指定列数。对于 int arr[3][5],正确的数组指针定义为:

int (*parr)[5] = arr;

解析逻辑:(*) 表示这是一个指针,指向一个拥有 [5] 个元素的数组,每个元素类型为 int。

第2步:识别复杂声明的含义

针对复杂声明 int (*parr[2])[3],需注意 [] 优先级高于 *,因此它不是“包含两个指针的数组”,而是:

  1. parr 是一个数组,有2个元素。
  2. 这些元素是 int (*)[3] 类型的指针,即“数组指针”。
  3. 它们指向的元素组包含3个int整数。

代码验证如下:

#include <stdio.h>
int main()
{
    int arr1[3] = {1,2,3};
    int arr2[3] = {2,3,4};
    int (*parr[2])[3] = {&arr1, &arr2}; // 数组指针数组
    printf("&arr1 = %p\n&arr2 = %p\n",&arr1,&arr2);
    for (int i = 0; i < 2; i++) {
        printf("parr[%d] = %p\n",i,parr[i]);
    }
    return 0;
}

运行结果为:

2、数组指针与二维数组的使用约束

再次强调:在二维数组 int arr[][N] 中,第一维(行数)可省略,第二维(列数)不可省略。这是因为编译器必须知道每一行的长度才能正确计算索引偏移量。

// 合法:允许省略行数
int arr[][3] = {{1,2,3},{1,2,3}};

// 非法:不允许省略列数
int arr[2][] = {{1,2,3},{1,2,3}}; // 编译错误

第3步:二维数组作为函数参数

将二维数组传递给函数时,通常采用以下两种方式,核心在于确保行/列大小的确定性以进行边界访问。

方式A:显式声明数组形式

这是最常用的方式,形参直接声明为数组。注意必须在函数内或外部宏定义控制循环边界。

#define ROW 2
#define COLUMN 3
void arrReset(int arr[ROW][COLUMN], int row, int col) {
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            arr[i][j] = 0;
        }
    }
}
int main()
{
    int arr[ROW][COLUMN] = {{1,2,3},{1,2,3}}; 
    arrReset(arr, ROW, COLUMN);
    // 打印结果验证归零...
    return 0;
}

方式B:使用指针形式传参

使用数组指针可以更灵活地处理不同类型的二维数组,避免在函数签名中硬编码列数限制。

#define ROW 2
#define COLUMN 3
void arrReset(int(*parr)[COLUMN], int row, int col) { 
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            // 以下写法在语义上是等价的:
            // parr[i][j] = 0;         
            // (*(parr + i))[j] = 0;   
            *(*(parr + i) + j) = 0;
        }
    }
}

注意:在使用指针访问元素时,*(parr[i] + j) 是完全合法的,因为 parr[i] 解引用后即为该行首元素地址。arrReset 依然需要传入实际的行数和列数以辅助循环控制。

排错:不要混淆指针数组与二维数组

初学者常犯的一个错误是将“指向指针的数组”强行赋给“二维数组”。这两者在内存结构中是不同的:

  • 二维数组:一块连续的内存空间。
  • 指针数组:存储多个独立内存块地址的数组,这些内存块可能不连续。

错误的尝试示例:

int arr1[3] = {1,2,3};
int arr2[3] = {4,5,6};
int* arr3[2] = {arr1, arr2}; // 指针数组,存放的是地址
int arr4[2][3] = arr3;       // 编译错误!无法直接用指针数组初始化二维数组

以上就是关于C++二维数组与数组指针的详细解析,更多关于C++基础语法的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。