C语言数组知识点
一、数组的基本概念
1.定义
数组是相同数据类型元素的集合,通过连续内存存储,支持高效访问。
核心特点:
-
元素类型相同
-
内存连续分配
-
通过下标访问(从
0
开始)
2.分类
- 一维数组:线性结构(如
int arr[5]
) -
多维数组:嵌套结构(如二维数组
int arr[3][4]
)
二、一维数组
1.声明与初始化
声明格式:数据类型 数组名[元素个数];
int arr[5]; // 声明未初始化(局部变量元素值为随机数)
static int s_arr[3]; // 静态数组未初始化元素默认为0
-
初始化方式:
2.内存特性初始化类型 示例代码 说明 完全初始化 int arr[3] = {1, 2, 3};
所有元素显式赋值 部分初始化 int arr[5] = {1, 2};
剩余元素自动补0(仅全局/静态数组有效) 长度自动推断 int arr[] = {10, 20, 30};
等价于 arr[3]
连续存储:相邻元素地址差为 sizeof(元素类型)
。
int arr[3] = {1, 2, 3};
printf("地址差:%td\n", (char*)&arr[1] - (char*)&arr[0]); // 输出:4(假设int占4字节)
-
越界风险:C语言不检查数组边界,越界访问可能导致程序崩溃或数据污染。
int arr[3] = {1}; arr[3] = 100; // 危险操作!覆盖未知内存区域
3.数组名与指针关系
数组名本质:数组名在大多数场景下退化为指向首元素的指针。
int arr[5] = {0};
int *p = arr; // 合法,p指向arr[0]
int (*p_arr)[5] = &arr; // 数组指针类型
例外情况:
-
sizeof(arr)
:返回整个数组的字节大小(如int[5]
返回20
) -
&arr
:返回指向整个数组的指针(类型为int(*)[5]
)
三、二维数组
-
声明与初始化
声明格式:数据类型 数组名[行数][列数];
int matrix[3][4]; // 3行4列的二维数组
-
初始化方式:
2.内存布局初始化类型 示例代码 说明 完全初始化(分行) int m[2][3] = {{1,2,3}, {4,5,6}};
每行显式赋值 连续初始化 int m[2][3] = {1,2,3,4,5,6};
按行优先顺序填充 部分初始化 int m[3][4] = {{1}, {0,5}, [2]={9,8}};
C99支持指定行初始化
行优先存储:二维数组本质是一维数组的嵌套,内存按行连续分配。
int arr[2][3] = {{1,2,3}, {4,5,6}};
// 内存地址验证
printf("行间地址差:%td\n", (char*)&arr[1][0] - (char*)&arr[0][0]); // 输出:12(假设int为4字节)
多维数组的指针视角
二维数组名退化为指向第一行(子数组)的指针:
int arr[2][3];
int (*p_row)[3] = arr; // p_row指向第一行(类型为int(*)[3])
四、动态数组
1.动态内存分配
使用 malloc
、calloc
、realloc
和 free
实现动态数组:
// 动态创建数组
int *dyn_arr = (int*)malloc(10 * sizeof(int));
if (dyn_arr == NULL) {
perror("内存分配失败");
exit(EXIT_FAILURE);
}
// 扩容数组(从10元素扩展到20)
int *temp = (int*)realloc(dyn_arr, 20 * sizeof(int));
if (temp != NULL) {
dyn_arr = temp;
} else {
free(dyn_arr); // 扩容失败需释放原内存
}
// 释放内存
free(dyn_arr);
2.变长数组(VLA)
-
C99标准支持:数组长度在运行时确定(部分编译器可能不支持)。
void func(int n) { int vla[n]; // 变长数组 // ... } // 生命周期结束后自动释放
五、数组的常见用法
1.遍历数组
-
一维数组遍历:
int arr[5] = {1,2,3,4,5}; for (int i=0; i<sizeof(arr)/sizeof(arr[0]); i++) { printf("%d ", arr[i]); }
-
二维数组遍历:
int matrix[3][4] = {0}; for (int i=0; i<3; i++) { for (int j=0; j<4; j++) { matrix[i][j] = i + j; } }
2.数组作为函数参数
-
传递方式:数组名退化为指针,需额外传递长度信息。
// 一维数组作为参数 void printArray(int arr[], int len) { // 等价于int *arr for (int i=0; i<len; i++) { printf("%d ", arr[i]); } } // 二维数组作为参数(需指定列数) void printMatrix(int mat[][4], int rows) { for (int i=0; i<rows; i++) { for (int j=0; j<4; j++) { printf("%d ", mat[i][j]); } } }
六、注意事项与常见错误
1.常见错误
-
越界访问:导致未定义行为(崩溃或数据损坏)
int arr[3] = {1,2,3}; arr[3] = 4; // 错误!越界访问
-
未初始化的局部数组:元素值为随机数
int arr[5]; printf("%d", arr[0]); // 输出不确定的值
2.数组与指针的陷阱
-
数组名不可修改:
int arr[5] = {0}; arr++; // 错误!数组名不是左值
-
指针与数组长度计算:
int arr[5]; int *p = arr; printf("数组长度错误计算:%zu\n", sizeof(p)/sizeof(int)); // 输出:1或2(指针大小)
七、代码示例:数组综合应用
#include <stdio.h>
#include <stdlib.h>
// 动态创建并操作二维数组
void dynamicMatrixDemo() {
int rows = 3, cols = 4;
// 动态分配内存
int **matrix = (int**)malloc(rows * sizeof(int*));
for (int i=0; i<rows; i++) {
matrix[i] = (int*)malloc(cols * sizeof(int));
}
// 赋值与打印
for (int i=0; i<rows; i++) {
for (int j=0; j<cols; j++) {
matrix[i][j] = i * cols + j;
printf("%2d ", matrix[i][j]);
}
printf("\n");
}
// 释放内存
for (int i=0; i<rows; i++) {
free(matrix[i]);
}
free(matrix);
}
int main() {
dynamicMatrixDemo();
return 0;
}
总结
核心原则:数组是C语言中连续内存块的直接体现,需手动管理内存与边界。
关键区别:
-
静态数组(编译时确定大小) vs 动态数组(运行时分配)
-
一维数组(线性访问) vs 多维数组(嵌套结构)
安全实践:
-
始终检查数组边界
-
动态内存分配后验证指针有效性
-
使用
sizeof(arr)/sizeof(arr[0])
计算静态数组长度
通过掌握数组的内存布局、指针关系及动态管理,可高效处理批量数据,同时避免常见陷阱。