数组初探:一维数组的定义、初始化与遍历

数组这东西,说白了就是一块连续的内存空间。我刚开始学C语言的时候,总觉得数组就是个「能存一堆数据的变量」,后来踩了几次坑才真正理解——数组的灵魂在于「连续」这两个字

你想想看,如果让你存100个学生的成绩,难道要定义100个单独的变量?score1, score2, score3... 写到第50个你就想砸键盘了。数组就是来解决这个问题的。

一维数组的定义

定义数组的语法很简单:

类型 数组名[元素个数];

举个例子:

int scores[100];      // 存放100个整数
float prices[50];     // 存放50个浮点数
char name[20];        // 存放20个字符

这里有个关键点——元素个数必须是常量。我在早期项目里犯过这个错:

int n = 100;
int arr[n];  // 这样写?在C99之前是错的!

嗯,C99标准之后支持了变长数组(VLA),但很多嵌入式编译器还不支持。我个人习惯还是用宏定义或者常量:

#define MAX_SIZE 100
int arr[MAX_SIZE];

数组的初始化

初始化有好几种玩法,我按常用程度给你排个序:

1. 完全初始化

int a[5] = {1, 2, 3, 4, 5};

这个没啥好说的,一一对应。

2. 部分初始化

int b[5] = {1, 2};  // 等价于 {1, 2, 0, 0, 0}

注意了!未显式初始化的元素会被自动赋值为0。这个特性我经常用——想快速把数组清零?直接 int arr[100] = {0}; 搞定。

3. 省略大小初始化

int c[] = {10, 20, 30};  // 编译器自动推断大小为3

这个写法很省事,但有个坑——你没法通过 sizeof(c)/sizeof(c[0]) 以外的方式知道数组大小。我曾经在接手一个老项目时,看到别人这么写,结果数组实际大小跟我想的不一样,排查了半天。

重要提醒: 数组一旦定义,大小就固定了。C语言不会帮你做越界检查,你写 arr[100] 它也不会报错,但后果自负。

数组在内存中的存储

这是理解数组的关键。咱们画个图看看:

一维数组 int arr[5] = {10, 20, 30, 40, 50} 内存布局 10 20 30 40 50 arr[0] arr[1] arr[2] arr[3] arr[4] 0x1000 0x1004 0x1008 0x100C 0x1010 ← 连续内存,每个元素占4字节(int类型) → 数组名 arr 就是首元素地址 &arr[0],即 0x1000

看到没?arr[0]arr[4] 在内存里是紧挨着的。每个 int 占4个字节,所以地址依次相差4。这就是数组的「连续存储特性」。

小技巧: 数组名 arr 本身就是一个指针常量,指向第一个元素。所以 arr&arr[0] 是等价的。但注意,arr 不是变量,你不能写 arr++

数组的遍历

遍历数组最常用的就是 for 循环:

#include <stdio.h>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    
    // 正向遍历
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    
    // 反向遍历
    for (int i = 4; i >= 0; i--) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    
    return 0;
}

输出结果:

arr[0] = 10
arr[1] = 20
arr[2] = 30
arr[3] = 40
arr[4] = 50
arr[4] = 50
arr[3] = 40
arr[2] = 30
arr[1] = 20
arr[0] = 10

这里有个细节——循环变量 i 从0开始,到 n-1 结束。为什么不是从1开始?因为数组下标就是0-based的。我见过新手写成 for (i = 1; i <= 5; i++),结果 arr[5] 越界了,程序跑飞都不知道怎么回事。

警告: C语言不会检查数组越界!你写 arr[5] 它不会报错,但会访问到数组后面的内存。我曾经在一个通信协议栈里,因为数组越界写坏了相邻变量的值,导致整个协议栈崩溃,查了整整两天才找到原因。

数组大小计算

有时候你不知道数组有多大,可以用 sizeof 来算:

int arr[] = {2, 4, 6, 8, 10, 12, 14};
int size = sizeof(arr) / sizeof(arr[0]);  // 7

原理很简单:sizeof(arr) 是整个数组占的字节数,sizeof(arr[0]) 是单个元素占的字节数,一除就是元素个数。

但注意——这个技巧只在数组定义的作用域内有效。如果你把数组传给函数,它就会退化成指针,sizeof 就失效了。这个坑我后面讲指针的时候再细说。

实际项目中的经验

我在做嵌入式开发时,经常用数组来查表。比如温度传感器采集到的电压值,通过查表转换成温度:

// 电压值到温度的映射表
int voltage_to_temp[] = {
    -40, -30, -20, -10, 0, 10, 20, 30, 40, 50
};

// 假设采集到的电压索引是 4(对应0度)
int index = 4;
int temp = voltage_to_temp[index];  // 得到 0

这种查表法比实时计算快得多,尤其适合资源受限的嵌入式环境。

另外,我个人习惯在定义数组时,总是显式初始化。哪怕只是 int arr[100] = {0};,也比不初始化强。因为局部数组如果不初始化,里面存的是垃圾值,一不小心就出bug。

总结一下

数组的核心就三点:

  • 连续存储——元素在内存里一个挨一个
  • 下标从0开始——arr[0] 是第一个元素
  • 不会越界检查——自己管好自己的下标

记住这三点,数组这块就稳了。下一节我们聊聊二维数组,其实也就是「数组的数组」,理解了今天的内容,二维数组就是水到渠成的事。


公众号:蓝海资料掘金营,微信deep3321