目标:把 变长数组(VLA, variable length array) 与 复合字面量(compound literal) 用法、限制与替代方案讲清;顺带梳理 C99 的几项“改变写法”的特性:指定初始化器、// 注释、for 内声明、restrict、stdbool.h。
1)VLA:运行期决定维度的栈数组(可选特性)
void foo(size_t n){
int a[n]; // n 运行期决定
/* ... */
}
要点
- 生存期:自动存储期(出栈即销毁)。
- C11/C17 把 VLA 变为可选特性;MSVC 不支持,部分嵌入式禁用。
- 大小过大可能栈溢出;不适合不受控的外部输入。
函数形参中的 VLA(携带维度)
void add_mm(size_t r, size_t c, int A[r][c], const int B[r][c]){
for(size_t i=0;i<r;++i) for(size_t j=0;j<c;++j) A[i][j]+=B[i][j];
}
优点:形参天然知道“行宽”;缺点:可移植性差、栈限制。
跨平台替代
- 用 连续堆块:int (*A)[c] = malloc(sizeof *A * r);(见第 02/09 讲)
- 或固定上界:#define MAXC 128,形参 int (*A)[MAXC]。
2)复合字面量(compound literal):一行造临时对象
#include <stdio.h>
void print_pair(const int p[2]){
printf("%d,%d\n", p[0], p[1]);
}
print_pair((int[2]){3, 4}); // 栈上临时对象
struct S { int x,y; };
struct S s = (struct S){ .x=1, .y=2 }; // 直接初始化结构体
用途
- 传数组/结构体临时值
- 便捷构造返回值或参数
- 配合指定初始化器写表驱动更清晰
生存期
- 在块作用域:临时对象活到完整表达式结束。
- 在文件作用域:是静态存储期对象。
3)指定初始化器(designated initializer)
int a[10] = { [0]=1, [3]=7 }; // 其他元素自动为 0
struct S t = { .y=2, .x=1 }; // 顺序可调,更不易出错
优势:读写清晰、避免错位;表驱动配置时极其好用。
4)for 内声明与 // 注释
for (int i=0; i<10; ++i) { /* ... */ } // 作用域更小
// 单行注释(C99 起)
5)restrict:指针别名承诺(可大幅优化)
void axpy(size_t n, float a, float * restrict y, const float * restrict x){
for (size_t i=0;i<n;++i) y[i] += a*x[i];
}
语义:被 restrict 修饰的指针在其有效生存期内不与其他指针别名同一对象。
作用:让编译器放心做矢量化等优化。
前提:你必须保证承诺为真;否则是未定义行为。
6)stdbool.h 与布尔类型
#include <stdbool.h>
bool ok = true; if (!ok) return false;
可读性与接口语义更清晰。
7)组合示例:表驱动 + 复合字面量 + 指定初始化器
struct Op { const char *name; int (*fn)(int,int); };
int add(int a,int b){ return a+b; }
int mul(int a,int b){ return a*b; }
static struct Op ops[] = {
[0] = { .name="add", .fn=add },
[1] = (struct Op){ .name="mul", .fn=mul },
};
int apply(int which,int a,int b){
if ((unsigned)which >= sizeof ops/sizeof ops[0]) return 0;
return ops[which].fn(a,b);
}
8)自检清单
- VLA 仅在受控尺寸与可移植性可接受时使用
- 更通用:连续堆块替代二维 VLA
- 复合字面量用来构造临时结构/数组
- 指定初始化器让表定义更稳
- restrict 只在你能保证无别名时使用
- stdbool.h 提升接口可读性
9)迷你练习
1)把矩阵加法的 VLA 版本改成“连续堆块”版本。
2)用复合字面量一行构造 struct sockaddr_in(提示:用指定初始化器)。
3)为一个 SAX 风格解析器写一张表:用指定初始化器填 state × event → handler。
