C 一维数组
更新时间:2026-08-26。本文是
languages/c/主题入门层第 24 篇,数组主题开篇。数组是"同类型元素的连续排列"——连续,意味着它在内存里是一段紧挨着的空间,这也是后面理解指针、缓存、性能的基础。
本文要回答的问题
- 数组在内存里长什么样?为什么下标从 0 开始?
- 数组初始化有哪几种写法?
- 怎么用 sizeof 算数组有几个元素?
一、声明与内存布局
c
int scores[5]; // 声明一个能放 5 个 int 的数组在 64 位 Linux(int 4 字节)上,scores 占 20 字节连续内存:
text
scores[0] scores[1] scores[2] scores[3] scores[4]
|----4B----|----4B----|----4B----|----4B----|----4B----|
地址: 0x100 0x104 0x108 0x10c 0x110两个推论:
- 元素地址 = 数组起始地址 + 下标 × 元素大小。编译器算地址就是这个公式,所以下标从 0 开始最自然(
[0]就是首元素,不需要-1); scores[3]和*(scores + 3)等价——数组名是首元素地址。这是后面数组与指针的伏笔。
二、五种初始化
c
int a[5] = {1, 2, 3, 4, 5}; // 1. 全部指定
int b[5] = {1, 2}; // 2. 部分指定:其余补 0 → {1,2,0,0,0}
int c[5] = {0}; // 3. 全部为 0(最常用)
int d[] = {1, 2, 3}; // 4. 省略大小:编译器数出来 → 长度 3
int e[5]; // 5. 不初始化:局部数组是垃圾值!两条铁律:
- 声明时必须给定大小(或让编译器从初始化列表推导),大小必须是常量表达式(C99 之前);
- 局部数组不初始化 = 垃圾值。
int arr[5];里装的是栈上残留,跟未初始化变量一个道理。清零用int arr[5] = {0};。
三、访问与遍历
c
#include <stdio.h>
int main(void) {
int scores[5] = {88, 92, 75, 60, 99};
printf("%d\n", scores[0]); // 88:下标从 0 开始!
scores[2] = 80; // 修改元素
printf("%d\n", scores[2]); // 80
int sum = 0;
for (int i = 0; i < 5; i++) { // 遍历:下标 0..4
sum += scores[i];
}
printf("总和 %d 平均 %.1f\n", sum, sum / 5.0);
return 0;
}遍历的黄金模式:
c
for (int i = 0; i < 长度; i++) {
// 处理 arr[i]
}四、用 sizeof 求元素个数
c
int arr[10];
size_t n = sizeof(arr) / sizeof(arr[0]); // 40 / 4 = 10| 表达式 | 值(int 4 字节) |
|---|---|
sizeof(arr) | 40(整个数组) |
sizeof(arr[0]) | 4(一个元素) |
sizeof(arr) / sizeof(arr[0]) | 10(元素个数) |
这个技巧只在"数组还在当前作用域"时有效。一旦数组作为参数传进函数(衰减成指针),sizeof(arr) 就变成 8(指针大小)——这是 C 新手最容易懵的点,指针与数组会专门讲。传数组给函数时,必须同时传长度:
c
void print_scores(int arr[], int n) { // n 由调用者给
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
}五、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 下标从 1 开始写 | 第一个元素没处理 | 记住 [0] 是第一个 |
| 越界访问 | 读/写数组外内存 | 边界检查(下篇细讲) |
| 局部数组不初始化 | 垃圾值 | = {0} 或显式初始化 |
| 传参后 sizeof 求长度 | 拿到指针大小 8 | 显式传长度 |
| 声明大小用变量 | 编译错误(旧标准) | 用常量或动态分配 |
六、与本站主线衔接
一句话总结
数组是连续内存,arr[i] = 起始地址 + i × 元素大小,下标从 0 开始;初始化用 {0} 清零、长度用 sizeof(arr)/sizeof(arr[0])、遍历用 i < 长度——传参时数组退化成指针,长度必须另传,这条要刻进脑子。