C语言不完整声明、复合类型与0长度数组规范及代码片段疑问
设计动机
这段内容是C标准化过程中针对实际开发的常见需求做的规则权衡,核心出发点有两个:
- 兼容不完整类型的合并逻辑:实际开发中经常出现多次声明同一个数组/函数但信息不全的情况,比如先声明
extern int arr[];(未指定数组长度),后面再定义int arr[10];,复合类型和类型兼容规则就是为了让这类声明能被正确合并,不会报类型不兼容的错误。 - 兼顾零长数组的使用场景和原有标准原则:C标准一直有「长度为0的对象非法」的通用规则,所以不允许单独声明
int arr[0];这类变量。但当时开发者已经普遍使用「结构体尾部放短长度数组+动态分配额外空间」的方式实现变长结构体,标准就单独为这个场景开了口子,允许零长数组作为结构体成员的特殊用法,不用严格遵守零长度对象的禁用规则。
代码片段理解
这是C99正式引入柔性数组成员之前,开发者通用的变长结构体实现范式:
struct segment { short int count; char c[N]; }; struct segment * new_segment( const int length ) { struct segment * result; // 申请的内存 = 结构体本身占用的大小 + 额外需要的数组空间大小 result = malloc( sizeof(struct segment) + (length-N) ); result->count = length; return result; }
逻辑很简单:结构体本身的大小已经包含了长度为N的数组成员c的空间,如果你需要c能存储length个元素,只需要在结构体本身的大小基础上多申请length-N字节的内存即可,这些多出来的内存会紧跟在结构体后面,可以合法通过c的下标访问。
举个实际例子:如果N是1,那sizeof(struct segment)已经包含了c[0]的1字节空间,要是你需要存10个char,就多申请9字节,总空间刚好够存10个元素。
N的来源
N是代码里提前自行定义的宏常量,通常只有两种取值:
- 如果你要兼容最严格的C89标准,就把N定义为1:这是C89唯一允许的最小数组长度,缺点是结构体本身会多占1字节的冗余空间,计算申请内存大小的时候也要多减1。
- 如果你的编译器支持0长度数组扩展(大部分编译器比如GCC、Clang很早就支持),就把N定义为0:没有冗余空间,计算内存的时候直接加length就行,写起来更简单。
后来C99正式把这个用法标准化为柔性数组成员,要求直接写char c[];,不需要再手动定义N,也不用处理N的取值问题。
内容的提问来源于stack exchange,提问作者Michael Munta
相关产品推荐
相关产品推荐

