一维数组:从定义到实战
数组这东西,说白了就是一块连续的内存空间,里面存了一堆相同类型的数据。我刚开始学C语言时,总觉得数组不就是个“大箱子”嘛,有啥好讲的?后来做项目才发现,用不好数组,代码能写得又臭又长。
举个例子,你要存100个学生的成绩。没有数组?那就得定义100个变量:score1, score2, score3……写到第50个我就想摔键盘了。数组就是来解决这个问题的。
一维数组的定义与初始化
定义数组的语法很简单:
类型 数组名[元素个数];
比如:
int scores[100]; // 100个int,未初始化
float prices[50]; // 50个float,未初始化
char name[20]; // 20个char,未初始化
嗯,这里要注意:元素个数必须是常量。C99之前不支持变量做数组长度,虽然C99引入了变长数组(VLA),但我个人建议——别用。为啥?移植性差,而且栈上分配大数组容易崩。
初始化有几种玩法:
// 完全初始化
int a[5] = {1, 2, 3, 4, 5};
// 部分初始化,剩下的补0
int b[5] = {1, 2}; // 等价于 {1, 2, 0, 0, 0}
// 不指定长度,编译器自动算
int c[] = {1, 2, 3, 4, 5}; // 长度就是5
// 全部初始化为0
int d[100] = {0};
我的习惯:定义数组时一定初始化。哪怕全赋0也行。不然里面是随机值,调试时能把你坑哭。
数组的访问与遍历
数组下标从0开始,这个坑我见过太多人踩了。你定义 int a[5],能用的下标是0到4,没有a[5]这回事。
int arr[5] = {10, 20, 30, 40, 50};
// 访问单个元素
printf("%d\n", arr[0]); // 输出10
printf("%d\n", arr[4]); // 输出50
// 遍历——最常用的写法
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
我曾经踩过的坑:有一次写排序算法,循环条件写成了
i <= 5,结果数组越界。程序没立刻崩,但排序结果总是莫名其妙。查了两小时才发现是下标越界把栈上别的变量给改了。从此以后,我写循环都默念三遍:小于号,小于号,小于号。
数组名在大多数情况下会退化为指向首元素的指针。所以 arr 和 &arr[0] 是等价的。但要注意,sizeof(arr) 返回的是整个数组的大小,不是指针的大小。
int arr[5];
printf("%zu\n", sizeof(arr)); // 20(假设int占4字节)
printf("%zu\n", sizeof(&arr[0])); // 8(64位系统,指针大小)
二维数组:表格思维
二维数组,说白了就是“数组的数组”。你想想看,一维数组是一条线,二维数组就是一个表格——有行有列。
定义与初始化
// 定义一个3行4列的二维数组
int matrix[3][4];
// 初始化
int grid[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// 也可以写成一串,编译器会自动按行填充
int grid2[2][3] = {1, 2, 3, 4, 5, 6};
内存里怎么存的?行优先存储。也就是说,先存完第一行所有列,再存第二行。这一点很重要,遍历时按行访问效率最高。
// 高效遍历:外层行,内层列
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
性能小贴士:如果你把内外层循环反过来(外层列,内层行),CPU缓存命中率会大幅下降。我在做图像处理项目时,就因为这个顺序问题,性能差了3倍多。
数组作为函数参数
数组传参时,实际上传的是指针。这一点很多新手会懵——明明我传的是整个数组,怎么函数里 sizeof 就变成指针大小了?
// 这三种写法完全等价
void func1(int arr[], int size);
void func2(int *arr, int size);
void func3(int arr[10], int size); // 这里的10被忽略
所以,必须额外传数组长度。函数内部没法通过 sizeof 知道数组大小。
void print_array(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int data[] = {1, 2, 3, 4, 5};
int len = sizeof(data) / sizeof(data[0]); // 计算元素个数
print_array(data, len);
return 0;
}
二维数组传参时,必须指定第二维的大小:
void print_matrix(int mat[][4], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", mat[i][j]);
}
printf("\n");
}
}
为啥必须指定第二维?因为编译器需要知道每行有多少个元素,才能计算 mat[i][j] 的内存地址。
核心总结:
- 数组下标从0开始,遍历时用
i < n,不是i <= n - 数组名是常量指针,不能自增自减
- 传数组时一定要带长度参数
- 二维数组是行优先存储,遍历时外层行内层列效率最高