C 语言数组从入门到精通:一维数组、二维数组与内存存储全解析 前言数组是 C 语言中最基础也最核心的数据结构之一几乎所有的 C 语言程序都离不开数组。无论是存储一组成绩、处理字符串还是为后续学习指针和动态内存管理打基础掌握数组都是迈向 C 语言高手的关键一步。本章将从一维数组和二维数组两个维度出发系统讲解数组的创建、初始化、使用、内存存储以及常见编程练习帮助你建立扎实的数组知识体系为后续学习指针、字符串和复杂数据结构做好充分准备。1. 数组的概念与目标数组是C语言中最重要的数据结构之一它是一组相同类型元素的集合。本章的学习目标包括掌握一维数组和二维数组的使用理解一维数组和二维数组的数据存放方式为后期学习指针和数组打好基础能够熟练使用一维数组和二维数组进行代码编写掌握变长数组的使用和特点1.1 数组的基本概念概念数组是一组相同类型元素的集合。数组中存放的是 1 个或者多个数据但是数组元素个数不能为 0数组中存放的多个数据类型是相同的数组分为一维数组和多维数组多维数组一般比较多见的是二维数组2. 一维数组的创建和初始化2.1 数组创建一维数组创建的基本语法如下type arr_name[常量值];type数组中存放数据的类型可以是char、short、int、float 等也可以是自定义的类型arr_name数组的名字根据实际情况起有意义的名字[]中的常量值用来指定数组的大小根据实际需求指定示例存储某个班级20人的数学成绩intmath[20];其他类型和大小的数组charch[8];doublescore[10];2.2 数组的初始化数组在创建时给定初始值称为初始化一般使用大括号数据放在大括号中使用逗号隔开。// 完全初始化intarr[5]{1,2,3,4,5};intarr[]{1,2,3,4,5};// 数组大小还是5编译器会根据初始化的内容推算出来// 不完全初始化intarr2[6]{1};// 第一个元素初始化为1剩余的元素默认初始化为0// 错误的初始化 - 初始化项太多intarr3[3]{1,2,3,4};// 错误初始化项超过数组大小2.2.1 字符串和字符数组字符数组的初始化可以使用大括号也可以使用字符串#includestdio.hintmain(){chararr1[]{a,b,c};// 字符数组chararr2[]abc;// 字符串return0;}这两种初始化方式有重要差异arr2数组中多了一个\0字符。原因C 语言中的字符串是用双引号括起来的一串字符在字符串的末尾会隐藏一个\0字符这个\0字符是字符串的结束标志。#includestdio.hintmain(){chararr1[]abc;chararr2[]{a,b,c};printf(arr1 %s\n,arr1);// 输出abcprintf(arr2 %s\n,arr2);// 输出abc后跟乱码return0;}printf函数在使用%s占位符打印字符串时从头开始直到遇到\0才结束。arr2缺少\0字符导致打印后出现乱码。2.2.2 sizeof 和 strlen 的对比特性sizeofstrlen类型操作符库函数需要包含头文件 string.h功能计算操作数所占内存的大小单位是字节求字符串长度统计的是\0之前字符的个数关注点不关注内存中存放什么数据关注内存中是否有\0如果没有\0就会持续往后找可能会越界#includestdio.h#includestring.hintmain(){chararr1[]abc;chararr2[]{a,b,c};printf(strlen(arr1) %zu\n,strlen(arr1));// 输出3printf(strlen(arr2) %zu\n,strlen(arr2));// 输出随机值可能越界printf(sizeof(arr1) %zu\n,sizeof(arr1));// 输出4包含\0printf(sizeof(arr2) %zu\n,sizeof(arr2));// 输出3return0;}2.3 数组的类型数组也有类型数组算是一种自定义类型去掉数组名留下的就是数组的类型intarr1[10];// 类型int [10]intarr2[12];// 类型int [12]charch[5];// 类型char [5]3. 一维数组的使用3.1 数组下标C语言规定数组是有下标的下标从0开始。假设数组有n个元素最后一个元素的下标就是n-1。intarr[10]{1,2,3,4,5,6,7,8,9,10};C语言提供了下标引用操作符[]来访问数组元素#includestdio.hintmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};printf(%d\n,arr[7]);// 输出8arr[7]0;// 修改元素的值printf(%d\n,arr[7]);// 输出0printf(%d\n,arr[3]);// 输出4return0;}3.2 数组元素的遍历3.2.1 打印数组内容使用for循环产生所有下标来遍历数组#includestdio.hintmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};inti0;for(i0;i10;i){printf(%d ,arr[i]);}// 输出1 2 3 4 5 6 7 8 9 10return0;}思考练习如果要逆序打印数组的内容怎么处理间隔一个元素打印数组内容怎么处理如果在访问数组元素的时候下标超出了范围会怎么样3.2.2 给数组输入内容#includestdio.hintmain(){intarr[10];inti0;printf(请输入10个整数\n);for(i0;i10;i){scanf(%d,arr[i]);}printf(您输入的数组是\n);for(i0;i10;i){printf(%d ,arr[i]);}printf(\n);return0;}4. sizeof计算数组元素个数使用sizeof可以计算数组的大小进而计算数组的元素个数#includestdio.hintmain(){intarr[10]{0};printf(数组总大小%zu字节\n,sizeof(arr));// 输出40printf(单个元素大小%zu字节\n,sizeof(arr[0]));// 输出4intszsizeof(arr)/sizeof(arr[0]);printf(数组元素个数%d\n,sz);// 输出10return0;}这种方法的好处是不管数组怎么变化计算出的元素个数都会自动调整。5. 一维数组在内存中的存储了解数组在内存中的存储方式有助于深入理解数组#includestdio.hintmain(){intarr[10]{1,2,3,4,5,6,7,8,9,10};intszsizeof(arr)/sizeof(arr[0]);inti0;for(i0;isz;i){printf(arr[%d] %p\n,i,arr[i]);}return0;}输出结果示例arr[0] 00000003BDB1F638 arr[1] 00000003BDB1F63C arr[2] 00000003BDB1F640 arr[3] 00000003BDB1F644 arr[4] 00000003BDB1F648 arr[5] 00000003BDB1F64C arr[6] 00000003BDB1F650 arr[7] 00000003BDB1F654 arr[8] 00000003BDB1F658 arr[9] 00000003BDB1F65C结论数组在内存中是连续存放的。随着下标的增长地址由小到大变化每两个相邻的元素之间相差4个字节因为一个整型是4个字节。6. 一维数组练习6.1 练习1字符从两端向中间汇聚#includestdio.h#includestring.h#includewindows.h// 包含Sleep函数intmain(){chararr1[]welcome to bit!!!;chararr2[]#################;intleft0;intrightstrlen(arr1)-1;printf(%s\n,arr2);while(leftright){Sleep(1000);// 暂停1秒arr2[left]arr1[left];arr2[right]arr1[right];left;right--;printf(%s\n,arr2);}return0;}运行效果################# w###############! we#############!! wel###########!!! ... welcome to bit!!!6.2 练习2二分查找折半查找#includestdio.hintmain(){intarr[]{1,2,3,4,5,6,7,8,9,10};intleft0;intrightsizeof(arr)/sizeof(arr[0])-1;intkey7;// 要找的数字intmid0;// 记录中间元素的下标intfind0;while(leftright){midleft(right-left)/2;// 防止溢出if(arr[mid]key){rightmid-1;}elseif(arr[mid]key){leftmid1;}else{find1;break;}}if(1find){printf(找到了下标是%d\n,mid);}else{printf(找不到\n);}return0;}注意计算中间下标时使用mid left (right - left) / 2可以防止left right可能导致的整数溢出。7. 二维数组的创建7.1 二维数组的概念如果把一维数组作为数组的元素就形成了二维数组。二维数组以上的数组统称为多维数组。7.2 二维数组的创建语法形式type arr_name[常量值1][常量值2];例如intarr[3][5];doubledata[2][8];3表示数组有3行5表示每一行有5个元素int表示数组的每个元素是整型类型arr是数组名8. 二维数组的初始化8.1 不完全初始化intarr1[3][5]{1,2};// 第一个元素为1第二个为2其余为0intarr2[3][5]{0};// 所有元素初始化为08.2 完全初始化intarr3[3][5]{1,2,3,4,5,2,3,4,5,6,3,4,5,6,7};8.3 按照行初始化intarr4[3][5]{{1,2},{3,4},{5,6}};8.4 初始化时省略行但是不能省略列intarr5[][5]{1,2,3};intarr6[][5]{1,2,3,4,5,6,7};intarr7[][5]{{1,2},{3,4},{5,6}};9. 二维数组的使用9.1 二维数组的下标二维数组使用行和列两个下标来访问元素行和列都从 0 开始#includestdio.hintmain(){intarr[3][5]{1,2,3,4,5,2,3,4,5,6,3,4,5,6,7};printf(%d\n,arr[2][4]);// 输出7第3行第5列return0;}9.2 二维数组的输入和输出#includestdio.hintmain(){intarr[3][5];inti0,j0;// 输入printf(请输入 3 行 5 列的整数矩阵\n);for(i0;i3;i){for(j0;j5;j){scanf(%d,arr[i][j]);}}// 输出printf(您输入的矩阵是\n);for(i0;i3;i){for(j0;j5;j){printf(%d ,arr[i][j]);}printf(\n);}return0;}10. 二维数组在内存中的存储#includestdio.hintmain(){intarr[3][5]{0};inti0,j0;for(i0;i3;i){for(j0;j5;j){printf(arr[%d][%d] %p\n,i,j,arr[i][j]);}}return0;}输出结果示例arr[0][0] 00000073E4CFFB18 arr[0][1] 00000073E4CFFB1C arr[0][2] 00000073E4CFFB20 arr[0][3] 00000073E4CFFB24 arr[0][4] 00000073E4CFFB28 arr[1][0] 00000073E4CFFB2C arr[1][1] 00000073E4CFFB30 arr[1][2] 00000073E4CFFB34 arr[1][3] 00000073E4CFFB38 arr[1][4] 00000073E4CFFB3C arr[2][0] 00000073E4CFFB40 arr[2][1] 00000073E4CFFB44 arr[2][2] 00000073E4CFFB48 arr[2][3] 00000073E4CFFB4C arr[2][4] 00000073E4CFFB50结论二维数组在内存中也是连续存放的。每一行内部的每个元素都是相邻的地址之间相差4个字节跨行位置处的两个元素之间也是差4个字节。11. 二维数组练习11.1 随机点名程序#includestdio.h#includestdlib.h#includetime.hintmain(){// 学生名单charstudents[20][10]{张三,李四,王五,赵六,小明,小红,小刚,小丽,小华,小强,张伟,李芳,王磊,赵敏,陈晨,刘洋,孙丽,周涛,吴杰,郑爽};intindex10,index20;// 初始化随机数种子srand((unsignedint)time(NULL));// 生成两个不同的随机索引do{index1rand()%20;index2rand()%20;}while(index1index2);printf(本次被点到的同学是%s 和 %s\n,students[index1],students[index2]);return0;}