复合语句
复合语句(Compound Statement)是用花括号 { ... } 包围的一组语句和声明,在 C 语言中也被称为"代码块"。复合语句可以出现在任何需要单个语句的地方,它将内部声明的变量限制在块作用域内,是控制流结构(if、while、for 等)的载体,也是组织代码逻辑的基本单元。
基本语法
{
声明或语句
声明或语句
...
}
复合语句本身不需要分号结尾(但内部的语句需要):
{
int x = 10;
printf("%d\n", x);
} /* 不需要分号 */
块作用域
在复合语句内部声明的变量只在该块内可见,块结束时变量销毁:
int main(void)
{
int a = 10; /* 外层变量 */
{
int b = 20; /* 内层变量,只在这个块内可见 */
printf("a = %d, b = %d\n", a, b); /* 可以访问 a 和 b */
}
/* printf("%d\n", b); */ /* 错误:b 已不可见 */
printf("a = %d\n", a); /* a 仍然可见 */
return 0;
}
C99 允许在复合语句的任何位置声明变量(混合声明),不必像 C89 那样全部放在块开头:
{
int x = 10;
printf("x = %d\n", x);
double y = 3.14; /* C99:在语句后声明 */
printf("y = %f\n", y);
for (int i = 0; i < 5; i++) { /* C99:for 循环内声明 */
printf("%d ", i);
}
}
变量遮蔽
内层块可以声明与外层同名的变量,内层变量会遮蔽(Shadow)外层变量:
int x = 10;
{
int x = 20; /* 内层 x 遮蔽外层 x */
printf("inner x = %d\n", x); /* 输出 20 */
}
printf("outer x = %d\n", x); /* 输出 10 */
遮蔽可能导致混淆,应尽量避免。编译器通常会发出"shadows a local variable"的警告(-Wshadow)。
复合语句与控制流
if、else、while、for、do-while 等控制流语句的体可以是单个语句或复合语句:
if (x > 0)
printf("Positive\n"); /* 单语句体 */
if (x > 0) { /* 复合语句体 */
printf("Positive\n");
count++;
process(x);
}
始终使用花括号是防御性编程的好习惯,即使只有一条语句:
/* 危险:后续添加语句时容易出错 */
if (x > 0)
printf("Positive\n");
process(x); /* 这行不在 if 内! */
/* 安全 */
if (x > 0) {
printf("Positive\n");
}
/* 后续添加语句不会出错 */
if (x > 0) {
printf("Positive\n");
process(x); /* 正确地在 if 内 */
}
嵌套块
复合语句可以任意嵌套:
{
int sum = 0;
for (int i = 0; i < 10; i++) {
int value = get_value(i);
if (value > 0) {
sum += value;
}
}
printf("Sum = %d\n", sum);
}
每个块都有自己的作用域,内层可以访问外层变量,外层不能访问内层变量。
块与内存管理
局部变量(自动变量)在块结束时自动释放:
void process(void)
{
{
int buffer[1024]; /* 大数组 */
read_data(buffer);
process_data(buffer);
} /* buffer 在这里释放 */
/* 继续其他操作,buffer 的内存已回收 */
do_other_work();
}
在栈空间有限的环境中(如嵌入式系统),将大数组限制在最小作用域内可以减少栈的峰值使用。
C99 的 for 循环作用域
C99 规定 for 循环中声明的变量作用域仅限于循环体:
for (int i = 0; i < 10; i++) {
printf("%d\n", i); /* i 可见 */
}
/* printf("%d\n", i); */ /* 错误:i 在循环外不可见 */
这与 C++ 的行为一致,避免了循环变量污染外部命名空间。
常见错误
忘记花括号:
if (x > 0)
printf("A\n");
printf("B\n"); /* 这行总是执行 */
/* 正确 */
if (x > 0) {
printf("A\n");
printf("B\n");
}
变量作用域混淆:
for (int i = 0; i < 10; i++) {
int x = i * 2;
}
printf("%d\n", x); /* 错误:x 不可见 */
/* 正确 */
int x;
for (int i = 0; i < 10; i++) {
x = i * 2;
}
printf("%d\n", x); /* 正确 */
块后多余的分号:
if (x > 0); { /* 空语句 + 独立的块 */
printf("Positive\n"); /* 总是执行 */
}
if (x > 0); 中的分号让 if 体为空,后面的 { ... } 是一个独立的复合语句,不是 if 的体。
最佳实践
- 控制流语句的体始终使用花括号,即使只有一条语句
- 将变量声明在最小必要的作用域内
- 避免变量遮蔽(内层与外层同名)
- 利用块作用域管理临时资源(大数组、文件指针等)
- 嵌套层次不宜过深,超过 3-4 层考虑重构