当前位置: 首页 > news >正文

深入解析二维数组传参的本质

目录

一、理解二维数组的传参

二、二维数组的本质

1、要理解二维数组传参的其他形式,首先需要深入理解二维数组的本质:

2、根据"数组名是数组首元素地址"的规则:

三、二维数组传参的指针形式

解析 *(*(p + i) + j) 的工作原理

1. 理解指针 p 的类型

2. 分解 *(*(p + i) + j)

第一步:(p + i)

第二步:*(p + i)

第三步:*(p + i) + j

第四步:*(*(p + i) + j)

3. 内存布局示例

4. 等价关系

5. 为什么这样设计?

四、关键点总结

五、数组传参知识点整理和回顾

1、一维数组传参

2、二维数组的理解

3、二维数组传参

4、关键点


一、理解二维数组的传参

        当我们理解了数组指针的概念后,就能更好地理解二维数组传参的本质了。传统上,当我们需要将一个二维数组传递给函数时,通常会这样写:

#include <stdio.h>void test(int a[3][5], int r, int c) {int i = 0;int j = 0;for(i = 0; i < r; i++) {for(j = 0; j < c; j++) {printf("%d ", a[i][j]);}printf("\n");}
}int main() {int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7}};test(arr, 3, 5);return 0;
}

        在这个例子中,实参是二维数组,形参也写成二维数组的形式。但实际上,还有其他更本质的写法。


二、二维数组的本质

1、要理解二维数组传参的其他形式,首先需要深入理解二维数组的本质:

  1. 二维数组可以看作是一个"元素为一维数组"的数组

  2. 二维数组的每个元素实际上是一个一维数组

  3. 因此,二维数组的首元素是它的第一行,即一个一维数组

2、根据"数组名是数组首元素地址"的规则:

  • 二维数组的数组名表示的是第一行的地址

  • 这个地址是一个"一维数组"的地址

  • 对于int arr[3][5],第一行的类型是int [5]

  • 因此第一行地址的类型就是数组指针类型int(*)[5]


三、二维数组传参的指针形式

既然二维数组传参本质上传递的是第一行这个一维数组的地址,那么形参也可以写成指针形式:

#include <stdio.h>void test(int (*p)[5], int r, int c) {int i = 0;int j = 0;for(i = 0; i < r; i++) {for(j = 0; j < c; j++) {printf("%d ", *(*(p + i) + j)); // 等价于p[i][j]}printf("\n");}
}int main() {int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7}};test(arr, 3, 5);return 0;
}

解析 *(*(p + i) + j) 的工作原理

        这段代码中的 *(*(p + i) + j) 是访问二维数组元素的指针表示法,它完全等价于更直观的 p[i][j]。让我们一步步分解这个表达式的计算过程。

1. 理解指针 p 的类型

首先,p 是一个指向包含5个整数的数组的指针,声明为 int (*p)[5]。这意味着:

  • p 指向的是二维数组的一整行(一个包含5个int的一维数组)

  • p + 1 会移动到下一行的起始位置

2. 分解 *(*(p + i) + j)

让我们从内向外解析这个表达式:

第一步:(p + i)
  • p 指向二维数组的第0行

  • p + i 指向二维数组的第i行

  • 结果仍然是一个指向一维数组(行)的指针

第二步:*(p + i)
  • 对行指针解引用,得到该行的首元素地址

  • *(p + i) 等价于 p[i],它表示第i行的第一个元素的地址

  • 现在这个表达式降级为一个指向整数的指针(int*

第三步:*(p + i) + j
  • 现在我们在行内移动,+j 表示移动到该行的第j个元素

  • 这相当于 &p[i][j],即第i行第j列元素的地址

第四步:*(*(p + i) + j)
  • 最后解引用这个地址,得到实际的整数值

  • 这就是我们想要的 p[i][j] 的值

3. 内存布局示例

假设我们的二维数组在内存中的布局如下(地址是假设的):

行0: [1(1000), 2(1004), 3(1008), 4(1012), 5(1016)]
行1: [2(1020), 3(1024), 4(1028), 5(1032), 6(1036)]
行2: [3(1040), 4(1044), 5(1048), 6(1052), 7(1056)]

  • p 初始指向行0,值为1000

  • p + 1 指向行1,值为1020(因为一行有5个int,每个int 4字节,共20字节)

  • *(p + 1) 得到行1的首地址1020

  • *(p + 1) + 2 得到1020 + 2*4 = 1028(行1的第2个元素)

  • *(*(p + 1) + 2) 解引用1028,得到值4

4. 等价关系

以下表达式都是等价的:

  • *(*(p + i) + j)

  • p[i][j]

  • *(p[i] + j)

  • (*(p + i))[j]

5. 为什么这样设计?

这种设计反映了C语言中数组和指针的紧密关系:

  1. 二维数组是按行优先顺序存储的

  2. 数组名在大多数情况下会退化为指针

  3. 指针算术运算会自动考虑所指向类型的大小

理解这种指针表示法对于处理动态分配的二维数组、函数参数传递等高级场景非常重要。


四、关键点总结

  1. 二维数组传参的本质传递的是第一行一维数组的地址

  2. 形参的两种写法

    • 数组形式:int a[][5] (第二个维度不能省略)或 int a[3][5]

    • 指针形式:int (*p)[5]

  3. 注意事项

    • 第二维的大小必须指定,编译器需要知道每一行的大小

    • 第一维的大小可以省略

    • 指针形式更直接反映了传参的本质

        理解这一点对于处理多维数组和指针操作非常重要,特别是在需要动态分配内存或处理数组的数组时。


五、数组传参知识点整理和回顾

1、一维数组传参

  • 数组名是首元素的地址

  • 传参时传递的是首元素的地址

  • 函数参数可以写成数组形式或指针形式

2、二维数组的理解

  • 二维数组可以理解为一维数组的数组

  • 每一行是一个一维数组

  • 二维数组的首元素是第一行(整个一维数组)

  • 数组名是第一行的地址(即一维数组的地址)

3、二维数组传参

  • 传参时传递的是第一行这个一维数组的地址

  • 形参可以写成数组形式或指针形式

4、关键点

  • 无论一维还是二维数组传参,都需要深入理解数组名的含义

  • 数组名代表的是首元素的地址,但二维数组的"首元素"是整个第一行

  • 参数形式灵活,可以用数组语法或指针语法表示

http://www.dtcms.com/a/336695.html

相关文章:

  • 运动场和光流-动手学计算机视觉17
  • 正则表达式实用面试题与代码解析专栏
  • 【Nginx】限流设置
  • 二三层交换转发业务~基础汇总
  • Mysql笔记-错误条件\处理程序
  • SSM从入门到实践:1.1 Spring框架概述与IoC容器入门
  • 堆(Heap):高效的优先级队列实现
  • duiLib 解决点击标题栏中按钮无响应问题
  • ROS2基础
  • C语言零基础第19讲:自定义类型—联合体和枚举
  • 解锁Java开发神器:XXL-Job从入门到精通
  • BT_LE_ADV_CONN_ONE_TIME 参数详解
  • Spring 创建 Bean 的 8 种主要方式
  • [创业之路-556]:创新的本质是赚不确定性带来的潜在价值,把不确定性逐步转化确定性,周而复始。
  • 产品设计.Ai产品经理
  • 48.Seata认识、部署TC服务、微服务集成
  • 网络中的一些基本概念
  • Conda 环境 在AI 私有化部署 有怎么用?
  • 微信小程序 小白gps工具v0.01 使用说明
  • react echarts图表监听窗口变化window.addEventListener(‘resize’)与ResizeObserver()
  • python -基础(5)组合数据类型
  • 每日两道算法题:DAY3
  • java常见的数据加密
  • 当 AI 开始 “理解” 情感:情感计算技术正在改写人机交互规则
  • Linux 服务:iSCSI 存储服务配置全流程指南
  • 廖雪峰-Java教程-Part02
  • C#高级语法_委托
  • 力扣第463场周赛
  • 17-线程
  • uC/OS - III 系统DEBUG时内核对象统计信息