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

c语言9:从内存到实践深入浅出理解数组

数组是编程中最基础也最常用的数据结构之一,几乎所有编程语言都支持数组。今天我们就来全面了解数组的方方面面,包括它在内存中的存储方式、二维数组的特性、初始化方法、常见问题以及数组名的特殊含义。

什么是数组?

简单来说,数组是相同类型元素的有序集合。它能让我们用一个名称管理一组相关的数据,通过索引来访问各个元素。

例如,我们可以用一个数组存储班级所有学生的成绩,而不用为每个学生单独创建一个变量

数组在内存中的存储

数组最显著的特点是连续存储,这意味着数组元素在内存中占据连续的存储空间。

想象一下内存就像一排储物柜,数组就像其中连续的几个柜子,每个柜子放一个元素,它们之间没有其他数据。

#include<stdio.h>
int main()
{int arr[7] = { 0,1,2,3,4,5,6 };printf("arr[%d]->%p\n", 0, &arr[0]);printf("arr[%d]->%p\n", 1, &arr[1]);printf("arr[%d]->%p\n", 2, &arr[2]);printf("arr[%d]->%p\n", 3, &arr[3]);printf("arr[%d]->%p\n", 4, &arr[4]);printf("arr[%d]->%p\n", 5, &arr[5]);printf("arr[%d]->%p\n", 6, &arr[6]);return 0;
}

结果为:

arr[0]->0000008D599EF5D8
arr[1]->0000008D599EF5DC
arr[2]->0000008D599EF5E0
arr[3]->0000008D599EF5E4
arr[4]->0000008D599EF5E8
arr[5]->0000008D599EF5EC
arr[6]->0000008D599EF5F0

内存地址图示:

这种连续存储的特性让数组访问速度非常快,因为知道了第一个元素的地址和元素类型,就能直接计算出任意元素的地址。

一维数组的初始化

在 C 语言中,数组有多种初始化方式:

// 方法1:指定长度并初始化
int scores[5] = {90, 85, 95, 88, 92};// 方法2:不指定长度,编译器自动计算
int numbers[] = {1, 2, 3, 4, 5};// 方法3:部分初始化,未初始化的元素默认为0
int values[5] = {10, 20}; // 相当于 {10, 20, 0, 0, 0}// 方法4:初始化所有元素为0
int zeros[5] = {0};

数组名是首元素的地址

在 C 语言中,数组名代表了数组首元素的地址,这是一个非常重要的特性,除了两个特例(sizeof(数组名)与 &数组名)

1:sizeof(数组名)这里的数组表示整个数组,计算的是整个数组的大小,单位是字节

2:&数组名 ,这里是整个数组为单位的地址

#include <stdio.h>int main() 
{int arr[5] = {1, 2, 3, 4, 5};printf("数组名 arr 的值: %p\n", arr);printf("首元素 &arr[0] 的地址: %p\n", &arr[0]);// 数组名 + 1 表示下一个元素的地址printf("arr + 1 的值: %p\n", arr + 1);printf("第二个元素 &arr[1] 的地址: %p\n", &arr[1]);// 通过指针方式访问数组元素printf("*(arr + 2) = %d\n", *(arr + 2)); // 相当于 arr[2]printf("arr[2] = %d\n", arr[2]);return 0;
}

运行这段代码,你会发现数组名arr和首元素地址&arr[0]是一样的。这也是为什么我们可以通过指针来操作数组。

      数组名 arr 的值: 000000EB6C8FFBE8
首元素 &arr[0] 的地址: 000000EB6C8FFBE8arr + 1 的值:  000000EB6C8FFBEC
第二个元素 &arr[1] 的地址: 000000EB6C8FFBEC*(arr + 2) = 3arr[2] = 3

二维数组

二维数组可以理解为 "数组的数组",它有行和列的概念。比如int 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]

在内存中实际上是这样排列的:

matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3], matrix[1][0], matrix[1][1], ...

二维数组的初始化

// 方法1:完整初始化
int matrix[3][4] = 
{{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}
};// 方法2:简化写法,按顺序初始化
int matrix[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};// 方法3:部分初始化,其余元素为0
int matrix[3][4] = 
{{1},{5, 6},{9, 10, 11}
};// 方法4:可以省略第一维的大小,编译器会自动计算
int matrix[][] = 
{{1, 2, 3},{4, 5, 6},{7, 8, 9}
};

数组越界问题

数组越界是指访问了超出数组定义范围的索引。这是编程中常见的错误,而且在 C 语言中不会被编译器检查出来。

#include <stdio.h>int main() 
{int arr[5] = {1, 2, 3, 4, 5};// 合法访问:索引 0-4printf("arr[0] = %d\n", arr[0]);printf("arr[4] = %d\n", arr[4]);// 数组越界:索引 5 超出了定义的范围printf("arr[5] = %d\n", arr[5]); // 未定义行为return 0;
}

运行结果:

arr[0] = 1
arr[4] = 5
arr[5] = -858993460

这里的arr[5]直接访问了内存值转为int值

数组越界的危害:

  1. 访问到无关数据,导致结果错误
  2. 修改到其他变量的值,引发难以排查的 bug
  3. 可能导致程序崩溃
  4. 严重时可能被利用为安全漏洞

数组的遍历

遍历数组就是依次访问数组的每个元素,通常使用循环来实现

#include <stdio.h>int main() 
{int numbers[] = {10, 20, 30, 40, 50};int length = sizeof(numbers) / sizeof(numbers[0]); // 计算数组长度// 遍历一维数组printf("一维数组元素: ");for (int i = 0; i < length; i++) {printf("%d ", numbers[i]);}printf("\n");// 遍历二维数组int matrix[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}};int rows = sizeof(matrix) / sizeof(matrix[0]);int cols = sizeof(matrix[0]) / sizeof(matrix[0][0]);printf("二维数组元素:\n");for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {printf("%d ", matrix[i][j]);}printf("\n");}return 0;
}

上面代码中

1:sizeof(numbers) / sizeof(numbers[0]) 是一种计算数组行数的常用方法

2:sizeof(matrix[0])  / sizeof(matrix[0][0]) 是一种计算数组列数的常用方法

总结

数组是一种简单而强大的数据结构,理解它的工作原理对编程非常重要:

  1. 数组在内存中连续存储,这使得访问速度很快
  2. 数组名代表首元素的地址,可以通过指针操作数组
  3. 二维数组本质上是 "数组的数组",在内存中按行连续存储
  4. 数组越界是常见错误,需要特别注意避免
  5. 可以通过循环遍历数组元素,使用sizeof可以计算数组长度

掌握数组的使用是编程的基础,后续学习更复杂的数据结构(如链表、栈、队列等)时,很多概念都与数组有关联。

希望这篇文章能帮助你更好地理解数组!如果你有任何疑问或想了解更多细节,欢迎在评论区留言。

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

相关文章:

  • sglang使用笔记
  • 本地大模型编程实战(36)使用知识图谱增强RAG(2)生成知识图谱
  • clip——手写数字识别
  • commons-numbers
  • MySqL-day4_01(内置函数、存储过程、视图)
  • 用html5写一个手机ui
  • 2.canvas学习
  • 【系统架构设计(34)】计算机网络架构与技术基础
  • 计网1.2 计算机网络体系结构与参考模型
  • ML-Watermelonbook
  • E/E架构新课题的解决方案
  • 【CVPR 2025】用于密集图像预测的频率动态卷积
  • 整体设计 语言拼凑/逻辑拆解/词典缝合 之 1 表达词项的散列/序列/行列 (豆包助手)
  • FPGA学习篇——Verilog学习之半加器的实现
  • Python快速入门专业版(三十五):函数实战2:文件内容统计工具(统计行数/单词数/字符数)
  • CSS的文本样式二【文本布局】
  • redis配置与优化
  • STM32 单片机 - 中断
  • 【网络工程师】ACL基础实验
  • 小实验--LCD1602显示字符和字符串
  • Java 的双亲委派模型(Parent Delegation Model)
  • ​​[硬件电路-249]:LDO(低压差线性稳压器)专用于线性电源,其核心设计逻辑与线性电源高度契合,而与开关电源的工作原理存在本质冲突。
  • conda命令行指令大全
  • TCP三次握手与四次挥手
  • Python读取Excel中指定列的所有单元格内容
  • 【DMA】DMA入门:理解DMA与CPU的并行
  • Redis数据库(一)—— 初步理解Redis:从基础配置到持久化机制
  • Salesforce中的事件驱动架构:构建灵活可扩展的企业应用
  • OpenCV实现消除功能
  • Qt QValueAxis详解