
🔥铅笔小新z:个人主页
🎬博客专栏:C语言
💫滴水不绝,可穿石;步履不休,能至渊。

一、什么是数组?
数组 是一组相同类型元素的集合。从这个概念中我们可以得到两个关键信息:
- 数组里可以放 1个或多个 数据(但不能是 0 个)
- 数组里的所有数据,类型必须相同
打个比方:数组就像一排编号的储物柜,每个柜子里放的东西类型都一样。
数组分为:
- 一维数组 ------ 一排柜子
- 多维数组 ------ 多排柜子(最常见的是二维数组,像表格)
二、一维数组的创建和初始化
2.1 创建数组
创建数组的语法:
c
type arr_name[常量值];
type:数组中存放的数据类型(如int、char、double等)arr_name:数组名,自己取个有意义的名字[常量值]:数组的大小(能放几个元素)
c
int math[20]; // 创建一个能放20个整数的数组,叫 math
char ch[8]; // 创建一个能放8个字符的数组,叫 ch
double score[10]; // 创建一个能放10个小数的数组,叫 score
2.2 初始化数组
创建数组的同时给一些初始值,叫做初始化:
c
int arr1[5] = {1, 2, 3, 4, 5}; // 完全初始化:5个元素全部赋值
int arr2[6] = {1}; // 不完全初始化:第一个元素是1,剩下的默认是0
int arr3[3] = {1, 2, 3, 4}; // 错误:初始化项太多(数组只有3个位置)
2.3 数组的类型
数组也是有类型的。去掉数组名和大小,剩下的就是数组的类型? 不对,其实大小也是类型的一部分!
c
int arr1[10]; // 类型是 int [10]
int arr2[12]; // 类型是 int [12](和上面不同!)
char ch[5]; // 类型是 char [5]
💡 注意:
int [10]和int [12]是不同的类型!
三、一维数组的使用
3.1 数组下标
C语言中,数组元素通过下标来访问:
- 下标从 0 开始
- 有 n 个元素的数组,最后一个元素的下标是 n-1
c
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 下标: 0 1 2 3 4 5 6 7 8 9
用 [](下标引用操作符)访问元素:
c
#include <stdio.h>
int main()
{
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
printf("%d\n", arr[7]); // 访问下标为7的元素 → 输出 8
printf("%d\n", arr[3]); // 访问下标为3的元素 → 输出 4
return 0;
}
3.2 遍历数组(用 for 循环)
要访问数组中每一个元素 ,只要配合 for 循环生成所有下标就行:
c
#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++) // i从0到9,正好10个下标
{
printf("%d ", arr[i]); // 依次打印每个元素
}
return 0;
}
// 输出:1 2 3 4 5 6 7 8 9 10
3.3 给数组输入数据
c
#include <stdio.h>
int main()
{
int arr[10] = {0}; // 全部初始化为0
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;
}
四、一维数组在内存中的存储
数组在内存中是怎么存放的呢?我们来打印一下每个元素的地址看看:
c
#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]); // %p 以十六进制打印地址
}
return 0;
}
运行结果(不同电脑地址不同,但规律一样):
&arr[0] = 00D6F7D8
&arr[1] = 00D6F7DC ← 比上一个地址大4
&arr[2] = 00D6F7E0 ← 比上一个地址大4
...
结论:
- 数组元素在内存中是连续存放的(一个挨着一个)
- 相邻元素地址相差
4个字节(因为一个int占 4 字节) - 数组下标越大,地址越大(从低到高排列)
这个特性非常重要,后面学"指针"的时候会用到。
五、sizeof 计算数组元素个数
sizeof 是 C语言的一个关键字,用来计算类型或变量所占内存大小(单位是字节)。
计算整个数组的大小
c
#include <stdio.h>
int main()
{
int arr[10] = {0};
printf("%d\n", sizeof(arr)); // 输出 40
// 一个 int 占 4 字节,10 个 int 占 40 字节
return 0;
}
计算数组元素个数
用 sizeof(arr) 除以 sizeof(arr[0]):
c
#include <stdio.h>
int main()
{
int arr[10] = {0};
int sz = sizeof(arr) / sizeof(arr[0]); // 总大小 ÷ 一个元素的大小 = 元素个数
printf("%d\n", sz); // 输出 10
return 0;
}
💡 好处: 用这种方式得到数组元素个数,以后数组大小变了也不用手动改代码!
六、二维数组
6.1 什么是二维数组?
可以把二维数组想象成一张表格------有行有列。
int arr[3][5]; // 3行5列的二维数组
| 行\列 | 列0 | 列1 | 列2 | 列3 | 列4 |
|---|---|---|---|---|---|
| 行0 | |||||
| 行1 | |||||
| 行2 |
6.2 创建二维数组
c
int arr[3][5]; // 3行5列,共15个int元素
double data[2][8]; // 2行8列,共16个double元素
6.3 初始化二维数组
不完全初始化(未指定的元素默认为 0):
c
int arr1[3][5] = {1, 2}; // 第1个元素是1,第2个是2,其余都是0
int arr2[3][5] = {0}; // 所有元素都是0
完全初始化:
c
int arr3[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
// 第0行:1 2 3 4 5
// 第1行:2 3 4 5 6
// 第2行:3 4 5 6 7
按行初始化(更清晰):
c
int arr4[3][5] = {{1, 2}, {3, 4}, {5, 6}};
// 第0行:1 2 0 0 0
// 第1行:3 4 0 0 0
// 第2行:5 6 0 0 0
省略行数(不能省略列数):
c
int arr5[][5] = {1, 2, 3}; // 自动推断为1行
int arr6[][5] = {1,2,3,4,5,6,7}; // 自动推断为2行
int arr7[][5] = {{1,2}, {3,4}, {5,6}}; // 自动推断为3行
6.4 使用二维数组
通过行下标和列下标访问元素:
c
#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]); // 第2行第4列 → 输出 7
// 第0行:1 2 3 4 5
// 第1行:2 3 4 5 6
// 第2行:3 4 5 6 7 ← 第4列是7
return 0;
}
遍历整个二维数组(嵌套 for 循环):
c
#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;
int j = 0;
// 输入
for (i = 0; i < 3; i++) // 外层循环控制行
{
for (j = 0; j < 5; j++) // 内层循环控制列
{
scanf("%d", &arr[i][j]); // 输入每个元素
}
}
// 输出
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n"); // 每行结束后换行,保持表格形状
}
return 0;
}
6.5 二维数组在内存中的存储
同样,打印地址看看:
c
#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;
}
结论: 二维数组在内存中也是连续存放的!先放第 0 行的所有元素,紧接着放第 1 行,然后放第 2 行......每一行内部是连续的,行和行之间也是连续的。
内存布局:[行0: 0][行0: 1][行0: 2][行0: 3][行0: 4][行1: 0][行1: 1]...
七、C99 变长数组
在 C99 标准之前,创建数组时大小必须是常量:
c
int arr1[10]; // 可以
int arr2[3 + 5]; // 可以(常量表达式)
int n = 10;
int arr3[n]; // ❌ 旧标准不支持
C99 引入了变长数组(VLA) ,允许用变量指定数组大小:
c
int n = 0;
scanf("%d", &n); // 输入数组大小
int arr[n]; // 运行时才确定大小,这就是变长数组
变长数组的特点:
- 数组的大小在运行时才确定
- 不能初始化(因为大小不确定,没法给初始值)
- 数组的大小一旦确定就固定了,不是说"可变长"数组
⚠️ 注意: VS2022 不支持变长数组,但 gcc 编译器支持。
八、数组练习
练习1:字符从两端移动,向中间汇聚
效果:像电影开场一样,字符从两边逐渐显示出来。
c
#include <stdio.h>
#include <string.h> // 使用 strlen 需要这个头文件
#include <windows.h> // 使用 Sleep 需要这个头文件
int main()
{
char arr1[] = "welcome to bit..."; // 目标字符串
char arr2[] = "#################"; // 初始全是 #,长度和 arr1 一样
int left = 0; // 左下标
int right = 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); // 打印当前状态
}
return 0;
}
练习2:二分查找(折半查找)
场景: 在一个排好序(升序)的数组中,快速找到指定数字。
思路: 每次取中间的元素比较,如果比目标大,就在左半部分找;如果比目标小,就在右半部分找。每找一次,范围就缩小一半。
c
#include <stdio.h>
int main()
{
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 升序数组
int left = 0; // 左边界下标
int right = sizeof(arr) / sizeof(arr[0]) - 1; // 右边界下标
int key = 7; // 要找的数字
int mid = 0; // 中间下标
int find = 0; // 是否找到的标志
while (left <= right) // 还有查找范围
{
mid = (left + right) / 2; // 计算中间位置
if (arr[mid] > key) // 中间值比目标大
{
right = mid - 1; // 在左半部分找
}
else if (arr[mid] < key) // 中间值比目标小
{
left = mid + 1; // 在右半部分找
}
else // 找到了!
{
find = 1; // 标记为已找到
break; // 跳出循环
}
}
if (find == 1)
{
printf("找到了,下标是 %d\n", mid);
}
else
{
printf("找不到\n");
}
return 0;
}
💡 优化: 计算中间下标时,
(left + right) / 2在 left 和 right 都很大时可能溢出。更安全的写法是:mid = left + (right - left) / 2
二分查找的效率:
- 顺序查找:最坏需要查 n 次
- 二分查找:最坏只需要查 log₂n 次
比如在 1000 个数字中查找:
- 顺序查找:最多 1000 次
- 二分查找:最多 10 次(因为 2¹⁰ = 1024)
总结
| 知识点 | 要点 |
|---|---|
| 数组概念 | 相同类型元素的集合 |
| 一维数组 | type name[size],下标从0开始 |
| 二维数组 | type name[行][列],像个表格 |
| 下标访问 | 用 [] 操作符,如 arr[3] |
| 内存布局 | 数组元素在内存中连续存放 |
| sizeof | sizeof(arr)/sizeof(arr[0]) 计算元素个数 |
| 变长数组 | C99特性,大小可用变量指定 |
| 二分查找 | 针对有序数组的高效查找算法 |
