C99标准的主要改进
C99 是 C 语言发展史上的重要里程碑,它在保持 C 语言简洁本质的同时,引入了许多实用特性。本教程基于 C99 标准,因此这些特性贯穿全文。了解 C99 的新增内容,有助于你区分"传统 C"和"现代 C",也能理解为什么某些代码在旧编译器上无法通过。
单行注释
C89 只支持 /* ... */ 块注释,C99 引入了 // 单行注释,从 // 到行尾的内容都被视为注释:
int x = 10; // 这是单行注释
/*
这是块注释,
可以跨越多行
*/
// 单行注释特别适合简短的行尾说明
int sum = a + b; // 计算两数之和
注意:块注释不能嵌套,/* /* */ */ 在第二个 */ 处就结束了,第三个 */ 会导致语法错误。单行注释没有这个问题。
变长数组(VLA)
C89 要求数组长度必须是编译期常量。C99 允许局部数组的长度在运行时确定:
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]);
}
int main(void)
{
process(5); /* 创建长度为 5 的数组 */
process(10); /* 创建长度为 10 的数组 */
return 0;
}
VLA 分配在栈上,函数返回时自动释放。但栈空间有限,过大的 VLA 可能导致栈溢出。C11 将 VLA 改为可选特性,某些编译器可能不支持。
long long 类型
C99 引入了 long long int 类型,保证至少 64 位,用于处理大整数:
long long big = 1234567890123LL;
printf("%lld\n", big);
unsigned long long huge = 18446744073709551615ULL;
printf("%llu\n", huge);
<limits.h> 中定义了 LLONG_MIN、LLONG_MAX、ULLONG_MAX 等宏。
混合声明与代码
C89 要求所有变量声明必须放在代码块的开头。C99 允许声明与语句交织,在需要时才声明变量:
int main(void)
{
int n = 10;
printf("n = %d\n", n);
double arr[n]; /* C99:在语句后声明 */
for (int i = 0; i < n; i++) /* C99:for 循环内声明 */
arr[i] = i * 0.5;
return 0;
}
for (int i = 0; ...) 中的 i 作用域仅限于循环体,避免了循环变量污染外部命名空间。
指定初始化器
C99 允许按名称或索引初始化数组和结构体成员,不必按顺序:
int arr[5] = { [2] = 10, [4] = 20 }; /* arr = {0, 0, 10, 0, 20} */
struct Point { int x; int y; };
struct Point p = { .y = 20, .x = 10 }; /* 按成员名初始化 */
这在初始化稀疏数组或大型结构体的部分字段时特别有用。
复合字面量
C99 允许在表达式中直接创建匿名数组或结构体:
struct Point { int x; int y; };
/* 不用先声明变量,直接传递复合字面量 */
draw_line((struct Point){0, 0}, (struct Point){100, 200});
/* 数组复合字面量 */
int *p = (int[]){1, 2, 3, 4, 5};
复合字面量在函数体内具有自动存储期(函数返回后失效),在文件作用域具有静态存储期。
新的标准头文件
C99 引入了一批新头文件,提供更精确的类型和更丰富的功能:
| 头文件 | 用途 |
|---|---|
<stdbool.h> | bool、true、false |
<stdint.h> | 定宽整数类型:int32_t、uint64_t 等 |
<inttypes.h> | 定宽整数的 printf/scanf 格式宏 |
<complex.h> | 复数类型和运算 |
<tgmath.h> | 类型泛型数学宏 |
<fenv.h> | 浮点环境控制 |
#include <stdbool.h>
#include <stdint.h>
bool flag = true;
int32_t exact_int = 42; /* 保证 32 位,跨平台一致 */
uint64_t big = 10000000000ULL;
inline 函数
C99 的 inline 建议编译器将函数调用替换为函数体,减少调用开销:
inline int max(int a, int b)
{
return (a > b) ? a : b;
}
inline 只是建议,编译器可以忽略。与宏相比,inline 函数有类型检查、可调试、无副作用风险。
其他改进
- 布尔类型:
_Bool关键字,配合<stdbool.h>使用bool、true、false - 复数类型:
_Complex、_Imaginary,配合<complex.h>使用 - 柔性数组成员:结构体最后一个成员可以是未指定大小的数组
- 预定义标识符
__func__:当前函数名的字符串 - 可变参数宏:
#define LOG(fmt, ...) printf(fmt, __VA_ARGS__) - 十六进制浮点常量:
0x1.fffffep+127 - ** snprintf**:安全的格式化输出,防止缓冲区溢出
这些特性让 C99 在保持语言简洁的同时,大幅提升了表达力和安全性。本教程的所有代码都基于 C99 标准编写。