数组是 C 语言中最核心的数据结构之一,更是编程入门阶段不可或缺的基础知识点。本文将从核心概念、使用方法到实际应用,对 C 语言数组展开全面且系统的讲解,助力大家扎实掌握这一关键内容。

数组是由一组相同数据类型元素组成的集合,从这一核心定义中,可提炼出 3 个关键信息: 1. 数组可存储 1 个或多个元素,且元素个数不可为 0; 2. 数组内所有元素的数据类型必须完全一致; 2. 数组按维度可分为一维数组与多维数组,其中多维数组以二维数组最为常用;
1 type arr_name[常量值];
存储在数组中的数据被称为数组元素。在 C 语言中,数组创建时需明确指定元素类型与数组大小,其核心语法组成及规则如下:
type:指定数组元素的数据类型,支持基本数据类型(如 char、short、int、float 等),也可使用自定义类型(如结构体、枚举等);
arr_name 即数组的名称,命名需结合实际使用场景,遵循语义化原则(确保名称能清晰体现数组的用途,便于代码可读性);
[ ] 中的常量值:用于定义数组的容量(即元素总个数),需根据具体业务需求合理设定,且必须为编译期可确定的常量(如字面量、宏定义等);
int math[20]; // 存储20个数字
char ch[8]; // 存储8个字符
double score[10]; // 存储10个双精度浮点数在实际编程中,若数组在创建时需要预先赋予一些初始值,这一过程称为数组的初始化。那么,数组如何进行初始化呢?其初始化操作通常通过大括号 {} 实现:将需赋予的初始值按顺序放入大括号内即可。
// 完全初始化
int arr[5] = {1, 2, 3, 4, 5};
// 不完全初始化
int arr2[6] = {1}; // 第一个元素为1,其余自动初始化为0
// 错误示例:初始化项太多
int arr3[3] = {1, 2, 3, 4}; // 编译错误与普通变量一样,数组同样具备明确的数据类型—— “自定义类型“,其类型由 “元素类型 + 数组容量” 共同决定:去掉数组名后剩余的语法结构,即为该数组的具体类型。
int arr1[10]; // 类型为 int[10]
int arr2[12]; // 类型为 int[12]
char ch[5]; // 类型为 char[5]在掌握一维数组的基本语法后,我们明确其核心价值在于存储数据,而存储数据的最终目的,是为了更高效地对数据执行读取、修改、遍历等后续操作。那么,实际编程中我们该如何运用一维数组来实现这些操作呢?
C语言规定数组是有下标的,下标是从0开始的,假设数组有n个元素,最后⼀个元素的下标是n-1,下标就相当于数组元素的编号。 eg:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
在 C 语言中,数组元素的访问通过下标引用操作符 [ ] 实现。借助这一操作符,我们可通过元素下标便捷地访问数组中的任意元素:例如,访问下标为 7 的元素可写作 arr[7],访问下标为 3 的元素则写作 arr[3]。
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n", arr[7]);//8
printf("%d\n", arr[3]);//4
return 0;
}8 4
若我们需遍历并访问整个数组的所有元素,这该如何实现? 只需我们产生出数组所有元素的下标就可以了——通过 for 循环精准生成 0~9 的连续下标,再结合下标引用操作符逐一访问对应元素。
#include <stdio.h>
int main() {
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
return 0;
}
// 输出:1 2 3 4 5 6 7 8 9 101 2 3 4 5 6 7 8 9 10
掌握了数组的访问方法后,我们也可根据实际需求,向数组中手动输入指定的数据,具体实现方式如下:
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10}; //初始化
int i = 0;
for(i=0; i<10; i++)
{
scanf("%d", &arr[i]);//输入数值
}
for(i=0; i<10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}2 4 6 8 10 1 3 5 7 9 2 4 6 8 10 1 3 5 7 9
掌握前文关于数组的基础知识点后,我们已能熟练运用数组完成各类基础操作;若想进一步深入理解数组的底层逻辑,核心是要掌握其在内存中的存储方式。 我们依次打印数组元素的地址来看一下:
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int i = 0;
for(i=0; i<10; i++)
{
printf("&arr[%d] = %p\n ", i, &arr[i]);
}
return 0;
}&arr[0] = 0133F8D0 &arr[1] = 0133F8D4 &arr[2] = 0133F8D8 &arr[3] = 0133F8DC &arr[4] = 0133F8E0 &arr[5] = 0133F8E4 &arr[6] = 0133F8E8 &arr[7] = 0133F8EC &arr[8] = 0133F8F0 &arr[9] = 0133F8F4
从上述输出结果可分析得出:数组元素的地址随下标递增依次增大,且相邻元素的地址差值为 4 字节(这是因为在常见环境中,int 类型数据占用 4 字节存储空间)。由此可明确结论:数组元素在内存中是连续存放的。这一内存存储特性为后续通过指针访问数组提供了底层依据(具体原理将在指针章节详细展开,此处只需牢记这一结论即可)。

在遍历数组时,我们常需要知道元素的具体个数,C语言中能否通过程序直接计算呢?答案是肯定的,这一需求可借助 sizeof 关键字实现。
sizeof 是C语言的内置关键字,其核心功能是计算类型或变量所占用的内存字节数,同时也能直接获取整个数组的总内存大小。
举个例子来说:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%d\n", sizeof(arr));
return 0;
}40
这里输出的结果是40,计算的是数组所占内存空间的总大小,单位是字节。
结合前文知识点可知,数组所有元素的类型完全一致,因此只需算出单个元素的字节大小,即可推导出数组的总元素个数。实操中,选择数组首个元素计算其大小(如 sizeof(arr[0])),就能满足需求。
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%d\n", sizeof(arr[0]));//计算⼀个元素的大小,单位是字节
return 0;
}4
这里输出的结果是4,计算的是一个元素的大小,单位是字节 。
接下来将两者相结合就能计算出数组的元素个数了:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", sz);
return 0;
}10
这里输出的结果是10,表示数组有10个元素。
后续编写代码时,涉及数组元素个数的场景,无需手动写死固定数值 —— 采用上述计算方法(数组总大小 ÷ 单个元素大小),无论数组元素增减、内容修改,计算出的元素个数都会自动同步更新,能大幅提升代码的灵活性与可维护性。
前文介绍的数组均为一维数组,其元素通常为内置类型(如 int、char 等)。若以一维数组作为元素构成新数组,则该数组称为二维数组;同理,以二维数组作为元素构成的数组称为三维数组。以此类推,二维及以上的数组统称为多维数组。

type arr_name[行数][列数];
int arr[3][5]; // 3行5列的整型数组
double data[2][8]; // 2行8列的双精度数组在 C 语言中,创建变量或数组时预先赋予初始值的操作,称为初始化。那么,二维数组该如何初始化?其方式与一维数组类似,同样通过大括号 { } 实现。
// 不完全初始化
int arr1[3][5] = {1, 2};
int arr2[3][5] = {0};
// 完全初始化
int arr3[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
// 按行初始化
int arr4[3][5] = {{1,2}, {3,4}, {5,6}};
// 省略行数(编译器自动推断)
int arr5[][5] = {1, 2, 3};
int arr6[][5] = {{1,2}, {3,4}, {5,6}};
掌握二维数组的创建与初始化方法后,该如何运用二维数组呢? 二维数组的访问同样采用下标形式 —— 由于其具有行、列二维结构,只需精准锁定行下标与列下标,就能唯一定位数组中的某个元素。 C 语言明确规定,二维数组的行下标与列下标均从 0 开始计数,具体示例如下:
int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
图中最左侧的紫色数字代表行号,最上行的蓝色数字代表列号,需注意二者均从 0 开始计数。例如,当我们指定 “第 2 行、第 4 列” 时,即可快速定位到元素 7。
#include <stdio.h>
int main()
{
int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
printf("%d\n", arr[2][4]);
return 0;
}7
既然我们已经掌握二维数组单个元素的访问方法,那该如何遍历并访问整个二维数组的所有元素呢? 核心思路是按规律生成所有的行下标与列下标:以上一段代码中的arr数组为例,其行下标范围为0-2,列下标范围为 0~4,因此可通过嵌套循环(外层循环控制行、内层循环控制列)生成所有下标组合,进而实现全数组元素的访问。
#include <stdio.h>
int main()
{
int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };//初始化,后续的输入操作会覆盖这些初始值
int i = 0;//遍历⾏
//输⼊
for (i = 0; i < 3; i++) //产生⾏号
{
int j = 0;
for (j = 0; j < 5; j++) //产生列号
{
scanf("%d", &arr[i][j]); //输⼊数据
}
}
printf("\n");
//输出
for (i = 0; i < 3; i++) //产生⾏号
{
int j = 0;
for (j = 0; j < 5; j++) //产生列号
{
printf("%d ", arr[i][j]); //输出数据
}
printf("\n"); //打印一行后换行
}
return 0;
}假设我们输入 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150
10 20 30 40 50 60 70 80 90 100 110 120 130 140 150
与探究一维数组的内存存储方式类似,若想深入了解二维数组在内存中的存储特征,同样可通过打印其所有元素的地址来分析。 具体代码如下:
#include <stdio.h>
int main()
{
int arr[3][5] = { 0 };
int i = 0;
int 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]);
}
}
return 0;
}&arr[0][0] = 0000006112FDF8E8 &arr[0][1] = 0000006112FDF8EC &arr[0][2] = 0000006112FDF8F0 &arr[0][3] = 0000006112FDF8F4 &arr[0][4] = 0000006112FDF8F8 &arr[1][0] = 0000006112FDF8FC &arr[1][1] = 0000006112FDF900 &arr[1][2] = 0000006112FDF904 &arr[1][3] = 0000006112FDF908 &arr[1][4] = 0000006112FDF90C &arr[2][0] = 0000006112FDF910 &arr[2][1] = 0000006112FDF914 &arr[2][2] = 0000006112FDF918 &arr[2][3] = 0000006112FDF91C &arr[2][4] = 0000006112FDF920
从上述输出结果可分析得出,在二维数组中,每行内部的元素地址连续递增,相邻元素地址差值为4字节(与int类型的字节占用一致);且相邻行的首尾元素(如arr[0][4]与arr[1][0])地址也相差4字节。由此可明确结论:二维数组的所有元素在内存中呈连续线性排列,其存储方式是“按行连续、跨行也连续”的一维存储逻辑延伸。
