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

相关推荐
摇滚侠1 小时前
Java 饿汉式 单例模式
java·开发语言·单例模式
lbb 小魔仙1 小时前
工业数据困局的破局者:DolphinDB 如何让海量时序数据真正“跑“出价值
开发语言·人工智能·python·langchain
枫叶丹41 小时前
【HarmonyOS 6.0】Device Security Kit安全审计阻断功能深度解析
开发语言·安全·华为·harmonyos
读书札记20221 小时前
C++ switch..case语句中变量跨域问题探讨及解决方法
开发语言·c++
一轮弯弯的明月1 小时前
Spring AOP编程
java·开发语言·spring boot·笔记·spring aop·学习心得
ch.ju1 小时前
Java程序设计(第3版)第四章——什么是对象
java·开发语言
努力努力再努力wz1 小时前
【Redis入门系列】Redis基础命令详解:从客户端连接到数据读写、key 管理与过期机制
c语言·开发语言·数据结构·数据库·c++·redis·缓存
谙弆悕博士1 小时前
【附C源码】C语言实现散列表
c语言·开发语言·数据结构·算法·散列表·数据结构与算法
Lucky_ldy1 小时前
C语言学习:自定义类型-结构体
c语言·开发语言·学习