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

C 语言数组从入门到精通:一维数组、二维数组与内存存储全解析

前言

数组是 C 语言中最基础也最核心的数据结构之一,几乎所有的 C 语言程序都离不开数组。无论是存储一组成绩、处理字符串,还是为后续学习指针和动态内存管理打基础,掌握数组都是迈向 C 语言高手的关键一步。本章将从一维数组和二维数组两个维度出发,系统讲解数组的创建、初始化、使用、内存存储以及常见编程练习,帮助你建立扎实的数组知识体系,为后续学习指针、字符串和复杂数据结构做好充分准备。

1. 数组的概念与目标

数组是C语言中最重要的数据结构之一,它是一组相同类型元素的集合。本章的学习目标包括:

  1. 掌握一维数组和二维数组的使用
  2. 理解一维数组和二维数组的数据存放方式,为后期学习指针和数组打好基础
  3. 能够熟练使用一维数组和二维数组进行代码编写
  4. 掌握变长数组的使用和特点

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 的对比

特性sizeofstrlen
类型操作符库函数,需要包含头文件 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;}

思考练习

  1. 如果要逆序打印数组的内容,怎么处理?
  2. 间隔一个元素打印数组内容,怎么处理?
  3. 如果在访问数组元素的时候,下标超出了范围,会怎么样?
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};// 所有元素初始化为0

8.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;}
http://www.jsqmd.com/news/1284306/

相关文章:

  • ClickHouse托管Postgres:OLTP+OLAP,新能力解锁最佳数据平台
  • 千问新人福利!8元无门槛优惠券直接领,输入 千问新人福利uqo6UY 免费领券,实测可用!
  • 北大等多机构联手破解AI视频“边想边画“难题:让机器先谋后动
  • 权限日志没搞定,GraphRAG 上线第一天就崩了:一次需求评审后的工程化反思
  • AI代理技能开发实战:从零搭建企业级智能任务处理系统
  • DIY桌面龙卷风发生器:从伯努利原理到流体力学实践
  • 智能导盲杖开发全解析:从传感器融合到嵌入式系统实现
  • 全自动评价系统账号封的太严重-----干不下去了
  • 2026 年更新:凤冈可靠的手动推拉雨棚加工厂怎么联系,你还在花大价钱焊固定棚?这玩意儿帮你省出半年房租,还能随用随收不占地方-新航户外遮阳 - 企业推荐官【认证】
  • 3步彻底解决ThinkPad风扇噪音:TPFanCtrl2智能控制方案
  • RAG文档切分
  • Halcon与WPF融合的机器视觉开发框架解析
  • 基于Arduino与3D打印的低成本四轴机械臂DIY全攻略
  • AI智能体时代已经到来 普通开发者如何抓住下一波技术红利
  • LTE Cat 1bis模块与ARM Cortex-M4的物联网通信方案
  • 国产高精度标定设备新选择:弘澧科技联合标定系统全解析(附性能对比与实测数据)
  • 2026 上半年论文降重行业全景盘点:主流 AIGC 检测平台算法迭代亮点与对应避坑方案
  • 技术选型年度复盘:2026年最值得投资的5个技术基础设施
  • 航空航天、低空与工业控制场景:泽天智航全域算力应用与市场观察
  • 麦小昆小车视觉巡线走方阵:从PID控制到状态机的完整实现
  • PyTorch 张量基础(一)
  • 微电网两阶段鲁棒优化调度与Matlab实现
  • 高效智能英雄联盟工具深度解析:League Akari完整实战指南
  • 网络上今年刚刚出现的新事物
  • (2026最新)芜湖本地人必选的靠谱漏水检测维修推荐:正规防水补漏防水-卫生间/厨房/屋顶/阳台/外墙渗漏水精准测漏,本地人的信赖之选 - 安佳防水
  • 会计专业学生找工作,怎么提升简历竞争力?
  • MiniSpring框架学习笔记-Pointcut:如何批量匹配代理方法?
  • SmartStor如何备份和恢复操作系统文件
  • 区块链服务商排名调整,场景深度绑定成关键变量
  • AI 写歌 App 推荐:2026中文商用写歌工具横向对比