【C语言】数组详解

🔥铅笔小新z:个人主页

🎬博客专栏:C语言

💫滴水不绝,可穿石;步履不休,能至渊。


一、什么是数组?

数组 是一组相同类型元素的集合。从这个概念中我们可以得到两个关键信息:

  1. 数组里可以放 1个或多个 数据(但不能是 0 个)
  2. 数组里的所有数据,类型必须相同

打个比方:数组就像一排编号的储物柜,每个柜子里放的东西类型都一样。

数组分为:

  • 一维数组 ------ 一排柜子
  • 多维数组 ------ 多排柜子(最常见的是二维数组,像表格)

二、一维数组的创建和初始化

2.1 创建数组

创建数组的语法:

c 复制代码
type arr_name[常量值];
  • type:数组中存放的数据类型(如 intchardouble 等)
  • 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
...

结论:

  1. 数组元素在内存中是连续存放的(一个挨着一个)
  2. 相邻元素地址相差 4 个字节(因为一个 int 占 4 字节)
  3. 数组下标越大,地址越大(从低到高排列)

这个特性非常重要,后面学"指针"的时候会用到。


五、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特性,大小可用变量指定
二分查找 针对有序数组的高效查找算法

相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai7 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜7 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa7 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio