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

C语言二维数组

一、示例

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

二、初始化

三、数组越界:

四、数组作为函数参数:

五、二维数组的使用

六、二维数组在内存中的存储

二维数组在内存中也是连续存储的。它遵循“行优先”的原则,即先存储第一行的所有元素,然后是第二行的所有元素,依此类推。

七、注意事项:

  • 数组名是地址:数组名本身代表数组首元素的地址(即&arr[0])。它是一个指针常量,不能被修改(例如arr = some_other_address;是非法的)。
  • 数组不能整体赋值/拷贝:你不能像arr1 = arr2;这样直接将一个数组赋值给另一个数组。必须使用循环或memcpy等函数逐个元素复制。
  • 数组大小sizeof(数组名)可以获取整个数组占用的字节数。对于静态数组,可以用sizeof(整个数组) / sizeof(数组中一个元素)来计算元素个数,但这只在数组定义的作用域内有效,不能用于函数参数中的数组。

八、二维数组的指针

1. 二维数组的内存布局

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

这个matrix在内存中是连续存放的:1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12

每一行matrix[0],matrix[1],matrix[2]都可以看作是一个包含4个int一维数组

2. 二维数组名的衰变

当二维数组名matrix出现在表达式中时,它会衰变指向其第一行(即一个包含4个int的数组)的指针

  • matrix的类型是int [3][4](一个包含3行4列的二维数组)。
  • 当作为值使用时,matrix的值是&matrix[0],其类型是int (*)[4]。这个类型读作:“指向包含4个int元素的数组的指针”。
3. 声明和使用指向二维数组行的指针

我们可以显式地声明一个与二维数组名衰变后类型相同的指针。

示例代码:

#include <stdio.h> int main() { int matrix[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; // pm 是一个指向包含4个int元素的数组的指针 int (*pm)[4] = matrix; // 等价于 int (*pm)[4] = &matrix[0]; printf("二维数组的地址 (matrix): %p\n", (void*)matrix); printf("pm 指向的地址: %p\n", (void*)pm); printf("matrix[0] 的地址 (&matrix[0]): %p\n", (void*)&matrix[0]); printf("pm 的大小 (指针大小): %zu 字节\n", sizeof(pm)); printf("(*pm) 的大小 (一行数组的大小): %zu 字节\n", sizeof(*pm)); // 4 * sizeof(int) // 如何访问元素? // pm 指向第一行 {1, 2, 3, 4} // *(pm + 0) 就是第一行 {1, 2, 3, 4},类型是 int[4] // *(pm + 1) 就是第二行 {5, 6, 7, 8},类型是 int[4] // *(pm + i) 就是第 i 行,类型是 int[4] // (*(pm + i))[j] 就是第 i 行第 j 列的元素 // pm[i][j] 是 *(pm + i)[j] 的简写形式,与二维数组的访问语法一致 printf("\n通过 pm[i][j] 访问:\n"); for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { printf("%d ", pm[i][j]); // 等价于 (*(pm + i))[j] } printf("\n"); } // 指针算术 pm++; // pm 现在指向第二行 (matrix[1]) printf("\npm++ 后,pm 指向: %p\n", (void*)pm); printf("此时 pm[0][0] = %d\n", pm[0][0]); // pm[0][0] 等价于 matrix[1][0],输出 5 printf("此时 pm[0][1] = %d\n", pm[0][1]); // 输出 6 return 0; }
  • int (*pm)[4] = matrix;pm被初始化为指向matrix的第一行。
  • sizeof(*pm):返回*pm(即pm指向的整个行数组)的大小,这里是4 * sizeof(int)
  • pm[i][j]:这是访问二维数组的标准方式。pm + i计算出第i行的地址,*(pm + i)解引用得到第i行这个一维数组,最后[j]访问该行的第j个元素。
  • pm++:指针pm的步长是它所指向的对象的大小,即一行数组的大小4 * sizeof(int)个字节)。因此,pm++会让pm直接跳到下一行的起始地址。
4. 二维数组与函数参数

这是二维数组指针最重要的应用场景之一。

当我们将二维数组传递给函数时,数组的第一维大小可以省略,但第二维(列数)必须指定。这是因为函数需要知道每一行有多少个元素,才能正确地进行地址计算。

函数定义:

#include <stdio.h> // 方式1: 使用指针语法,明确表示指向行的指针 void printMatrix1(int rows, int (*matrix)[4]) { // 列数必须指定 printf("在 printMatrix1 中,matrix 的类型是 int(*)[4]\n"); for (int i = 0; i < rows; i++) { for (int j = 0; j < 4; j++) { printf("%d ", matrix[i][j]); // matrix[i][j] 等价于 *(*(matrix + i) + j) } printf("\n"); } } // 方式2: 使用数组语法,效果相同,更易读 void printMatrix2(int rows, int matrix[][4]) { // 列数必须指定 printf("在 printMatrix2 中,matrix 的类型也是 int(*)[4]\n"); for (int i = 0; i < rows; i++) { for (int j = 0; j < 4; j++) { printf("%d ", matrix[i][j]); } printf("\n"); } } // 方式3: 使用变长数组 (VLA) - C99 标准引入 void printMatrix3(int rows, int cols, int matrix[rows][cols]) { // 列数可以是变量 printf("在 printMatrix3 中,使用 VLA\n"); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { printf("%d ", matrix[i][j]); } printf("\n"); } } int main() { int my_matrix[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; int rows = 3, cols = 4; printMatrix1(rows, my_matrix); // 传递二维数组名 printf("---\n"); printMatrix2(rows, my_matrix); printf("---\n"); printMatrix3(rows, cols, my_matrix); return 0; }

关键点

  • 列数必须指定:无论是int (*matrix)[4]还是int matrix[][4],方括号中的4都不能省略。这告诉编译器如何计算matrix[i]的地址(即第i行的地址)。
  • 第一维可以省略int matrix[][4]int matrix[3][4]在函数参数中是等价的,都表示一个指向包含4个int的数组的指针 (int (*)[4])。
  • 为什么这样设计:因为二维数组在内存中是连续的,编译器需要知道每行的宽度(列数)才能从第0行的地址计算出第1行、第2行...的地址。
5. 总结
  • 二维数组名arr在传递给函数或赋值给指针时,会衰变为指向其第一行的指针,类型为int (*)[列数]
  • 声明这种指针的语法是数据类型 (*指针名)[列数]
  • 这种指针p支持p[i][j]的访问方式,其原理是*(*(p + i) + j)
  • 指针算术p++的步长是一行数组的大小
  • 在函数参数中,二维数组必须指定列数,这是实现arr[i][j]随机访问的关键。
http://www.jsqmd.com/news/695880/

相关文章:

  • HTML函数运行慢是硬件问题吗_HTML函数卡顿原因排查技巧【详解】
  • 安卓应用开发中协程作用域未正确取消问题详解
  • Qwen3-ASR-0.6B多场景落地指南:从边缘设备到云端集群部署
  • Qwen3.5-27B工业设计辅助:CAD截图理解+技术参数补全效果展示
  • 西门子TIA Portal V17实战:手把手教你用EnTalk PCIe板卡打通PROFINET与Modbus RTU
  • <iostream>
  • AI Agent开发者薪资倒挂现象:应届生比老员工高
  • 别再滥用Dynamic NavMesh了!UE4/UE5导航系统性能对比与正确配置指南
  • 告别手动测试:如何用CANoe的LIN一致性测试模块自动化你的ECU验证流程?
  • 2024年Mathorcup数学建模C题:从思路解析到代码实现的完整攻关指南
  • 基于多模态大模型的桌面自动化工具autoMate实战指南
  • 量子相位估计与Suzuki-Trotter分解在量子计算中的应用
  • 机器学习初学者必备工具链与实战指南
  • AI Agent开发者薪资天花板:年薪百万是什么水平
  • 如何让Windows和Office永远告别激活烦恼?KMS智能激活方案全解析
  • Python 进阶
  • Service Mesh(服务网格)介绍(将服务间通信复杂逻辑从业务代码中剥离,交由独立基础设施处理)Sidecar Proxy、数据平面、控制平面、Envoy、Istio、Linkerd
  • Meta计划5月裁员约10%,约8000人受影响,此前AI领域投资巨大
  • 学Simulink——基于Simulink的固态变压器(SST)多级协同控制​
  • 别再手动算了!用Matlab的dec2hex/dec2bin函数搞定进制转换(附硬件寄存器操作实例)
  • 第四章-10-变量作用域
  • 海康威视访客系统API避坑指南:从权限下发失败到动态二维码生成的5个常见问题
  • Web安全深度解析:文件上传漏洞的原理、攻击与防御
  • 并查集
  • YOLOv8改进 | Neck篇 | CVPR最新低照度图像增强模块HVI改进YOLOv8(有效涨点)
  • 13+Spring Native与GraalVM原生编译
  • ARM智能卡接口(SCI)架构与通信协议详解
  • 10款论文降AI工具实测:SpeedAI 100%AI率瞬清零,语义保留99%
  • 小升初英语衔接轻创业,KISSABC 落地全拆解
  • AI代理生产化部署:架构设计与性能优化实战