变长数组 VLA
变长数组(Variable Length Array,VLA)是 C99 引入的特性,允许数组的大小在运行时确定。VLA 分配在栈上,函数返回时自动释放。它让数组操作更灵活,但栈空间有限,过大的 VLA 可能导致栈溢出。C11 将 VLA 改为可选特性,某些编译器可能不支持。
基本用法
void process(int n)
{
int arr[n]; /* VLA:大小由参数 n 决定 */
for (int i = 0; i < n; i++)
arr[i] = i * i;
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
printf("\n");
}
int main(void)
{
process(5); /* 创建大小为 5 的数组 */
process(10); /* 创建大小为 10 的数组 */
return 0;
}
多维 VLA
VLA 支持多维,每个维度都可以是变量:
void print_matrix(int rows, int cols, int mat[rows][cols])
{
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++)
printf("%d ", mat[i][j]);
printf("\n");
}
}
int main(void)
{
int rows = 3, cols = 4;
int mat[rows][cols]; /* 二维 VLA */
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
mat[i][j] = i * cols + j;
print_matrix(rows, cols, mat);
return 0;
}
注意:VLA 参数中,维度参数必须出现在数组参数之前。
VLA 的限制
不能有初始化器:
int n = 5;
int arr[n] = {1, 2, 3}; /* 错误:VLA 不能有初始化器 */
/* 正确 */
int arr[n];
for (int i = 0; i < n; i++)
arr[i] = i;
不能在文件作用域:
int n = 10;
int arr[n]; /* 错误:VLA 只能在块作用域 */
sizeof 在运行时计算:
int n = 5;
int arr[n];
size_t sz = sizeof(arr); /* 运行时计算:n * sizeof(int) */
普通数组的 sizeof 在编译时计算,VLA 的 sizeof 在运行时计算。
VLA 与动态分配的选择
| 特性 | VLA | malloc |
|---|---|---|
| 分配位置 | 栈 | 堆 |
| 自动释放 | 是(函数返回) | 否(需 free) |
| 大小限制 | 栈空间(通常几 MB) | 可用内存(通常更大) |
| 初始化器 | 不支持 | 支持(memset) |
| 文件作用域 | 不支持 | 支持 |
| 可移植性 | C11 可选 | 所有 C 版本 |
/* VLA:小数组,自动管理 */
void process_small(int n)
{
if (n > 10000) return; /* 防止栈溢出 */
int arr[n];
/* ... */
}
/* malloc:大数组,手动管理 */
void process_large(int n)
{
int *arr = malloc(n * sizeof(int));
if (arr == NULL) return;
/* ... */
free(arr);
}
常见错误
栈溢出:
void func(void)
{
int n = 1000000;
int arr[n]; /* 4 MB,可能超出栈空间 */
}
VLA 参数维度顺序错误:
/* 错误 */
void func(int mat[rows][cols], int rows, int cols);
/* 正确 */
void func(int rows, int cols, int mat[rows][cols]);
在结构体中使用 VLA:
struct Data {
int n;
int arr[n]; /* 错误:结构体成员不能用 VLA */
};
最佳实践
- VLA 适合小数组(< 1 MB)
- 大数组用
malloc分配在堆上 - 使用 VLA 前检查大小,防止栈溢出
- 需要初始化器时用
malloc+ 循环/memset - 追求可移植性时避免 VLA(C11 可选)
- 多维 VLA 参数中,维度参数在数组参数之前