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 字符串和字符数组
字符数组的初始化可以使用大括号,也可以使用字符串:
#include<stdio.h>intmain(){chararr1[]={'a','b','c'};// 字符数组chararr2[]="abc";// 字符串return0;}这两种初始化方式有重要差异:arr2数组中多了一个'\0'字符。
原因:C 语言中的字符串是用双引号括起来的一串字符,在字符串的末尾会隐藏一个'\0'字符,这个'\0'字符是字符串的结束标志。
#include<stdio.h>intmain(){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 的对比
| 特性 | sizeof | strlen |
|---|---|---|
| 类型 | 操作符 | 库函数,需要包含头文件 string.h |
| 功能 | 计算操作数所占内存的大小,单位是字节 | 求字符串长度,统计的是\0之前字符的个数 |
| 关注点 | 不关注内存中存放什么数据 | 关注内存中是否有\0,如果没有\0,就会持续往后找,可能会越界 |
#include<stdio.h>#include<string.h>intmain(){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(包含'\0')printf("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语言提供了下标引用操作符[]来访问数组元素:
#include<stdio.h>intmain(){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循环产生所有下标来遍历数组:
#include<stdio.h>intmain(){intarr[10]={1,2,3,4,5,6,7,8,9,10};inti=0;for(i=0;i<10;i++){printf("%d ",arr[i]);}// 输出:1 2 3 4 5 6 7 8 9 10return0;}思考练习:
- 如果要逆序打印数组的内容,怎么处理?
- 间隔一个元素打印数组内容,怎么处理?
- 如果在访问数组元素的时候,下标超出了范围,会怎么样?
3.2.2 给数组输入内容
#include<stdio.h>intmain(){intarr[10];inti=0;printf("请输入10个整数:\n");for(i=0;i<10;i++){scanf("%d",&arr[i]);}printf("您输入的数组是:\n");for(i=0;i<10;i++){printf("%d ",arr[i]);}printf("\n");return0;}4. sizeof计算数组元素个数
使用sizeof可以计算数组的大小,进而计算数组的元素个数:
#include<stdio.h>intmain(){intarr[10]={0};printf("数组总大小:%zu字节\n",sizeof(arr));// 输出:40printf("单个元素大小:%zu字节\n",sizeof(arr[0]));// 输出:4intsz=sizeof(arr)/sizeof(arr[0]);printf("数组元素个数:%d\n",sz);// 输出:10return0;}这种方法的好处是:不管数组怎么变化,计算出的元素个数都会自动调整。
5. 一维数组在内存中的存储
了解数组在内存中的存储方式有助于深入理解数组:
#include<stdio.h>intmain(){intarr[10]={1,2,3,4,5,6,7,8,9,10};intsz=sizeof(arr)/sizeof(arr[0]);inti=0;for(i=0;i<sz;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:字符从两端向中间汇聚
#include<stdio.h>#include<string.h>#include<windows.h>// 包含Sleep函数intmain(){chararr1[]="welcome to bit!!!";chararr2[]="#################";intleft=0;intright=strlen(arr1)-1;printf("%s\n",arr2);while(left<=right){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:二分查找(折半查找)
#include<stdio.h>intmain(){intarr[]={1,2,3,4,5,6,7,8,9,10};intleft=0;intright=sizeof(arr)/sizeof(arr[0])-1;intkey=7;// 要找的数字intmid=0;// 记录中间元素的下标intfind=0;while(left<=right){mid=left+(right-left)/2;// 防止溢出if(arr[mid]>key){right=mid-1;}elseif(arr[mid]<key){left=mid+1;}else{find=1;break;}}if(1==find){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 开始:
#include<stdio.h>intmain(){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 二维数组的输入和输出
#include<stdio.h>intmain(){intarr[3][5];inti=0,j=0;// 输入printf("请输入 3 行 5 列的整数矩阵:\n");for(i=0;i<3;i++){for(j=0;j<5;j++){scanf("%d",&arr[i][j]);}}// 输出printf("您输入的矩阵是:\n");for(i=0;i<3;i++){for(j=0;j<5;j++){printf("%d ",arr[i][j]);}printf("\n");}return0;}10. 二维数组在内存中的存储
#include<stdio.h>intmain(){intarr[3][5]={0};inti=0,j=0;for(i=0;i<3;i++){for(j=0;j<5;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 随机点名程序
#include<stdio.h>#include<stdlib.h>#include<time.h>intmain(){// 学生名单charstudents[20][10]={"张三","李四","王五","赵六","小明","小红","小刚","小丽","小华","小强","张伟","李芳","王磊","赵敏","陈晨","刘洋","孙丽","周涛","吴杰","郑爽"};intindex1=0,index2=0;// 初始化随机数种子srand((unsignedint)time(NULL));// 生成两个不同的随机索引do{index1=rand()%20;index2=rand()%20;}while(index1==index2);printf("本次被点到的同学是:%s 和 %s\n",students[index1],students[index2]);return0;}