龙空技术网

小x讲解C语言:“零长度数组”的使用

小x互联网工程师 713

前言:

此刻朋友们对“c语言长度”大致比较关注,我们都需要学习一些“c语言长度”的相关资讯。那么小编也在网上网罗了一些有关“c语言长度””的相关内容,希望兄弟们能喜欢,兄弟们一起来了解一下吧!

C语言里面有一种稍显特别的零长度数组(Array)使用方式,在一般的C语言教材里面不一定会提到,但却非常实用,在操作系统的内核经常用到,在应用层的开发中也很有实用价值,这里通过一个例子演示,只要记住套路,一些场景下会非常方便高效。

在通常情况下,定义一个C语言的数组时是要指定数组的长度的,而且数组长度必须是常量,现代C的编译器一般支持在函数体内可变长的数组。

可变长的数组实现的具体细节,可以写一个简单的程序,然后反汇编看一下,如果稍懂一点汇编,知道那几个寄存器的用途就可以看明白了。

简单的可变长数组Demo

va.c

void variable_length_array(int n){	char string[n];	//do some thing	memset(string,0,n);	string[0] = 'c';	string[1] = '\0';	printf("[%s]\n",string);}

gcc -O2 -S va.c

variable_length_array:pushq	%rbpmovslq	%edi, %rdxxorl	%esi, %esimovq	%rsp, %rbpsubq	$16, %rsp <---- 修改栈寄存器movq	%fs:40, %raxmovq	%rax, -8(%rbp)xorl %eax, %eaxleaq 15(%rdx), %raxandq	$-16, %raxsubq	%rax, %rsp <---- 再次修改栈寄存器movq	%rsp, %rdi

上面的汇编我去掉一部分使重点突出,编译的时候选用 -O2 选项,这样生成的代码精简,如果不加优化开关生成的代码比较冗长,反而难读懂。

可是打开 -O2 选项会带来一个问题,必须在后面的代码中使用一下 char string[n], 否则打开优化开关的编译器会把char string[n] 优化掉。

如果看不懂汇编没关系,知道原理就行了,这种形式是利用了栈的特性,通过调整栈寄存器去改变数组的大小。

这种可变长数组有着比广泛的使用,但是要注意如果传入的 n 的值过大超过这个进程的栈空间的最大值,调用这个函数时进程就有可能崩溃。

可变长数组不是本文要重点讲解的内容,通过这个例子初学者可以体验一下C语言的数据和内存的关系。

下面正式讲解“零长度数组”

在实际开发中会有类似的场景,假如我们有一张表

name | int_value | string_valueAAA 111 Test1BBB 222 Test2CCC 333 Test3

对于这张表,我们希望用一个指针管理(申请/释放)一块内存,用这块内存装载上面这组数据,这块内存刚好装下这组数据,可以通过数组的下标访问这组数据的每条记录,当然还可以知道数组的长度。

当我们不需要这块内存的时候,可以直接释放。

上面提到,定义一个C语言的数组时通常是要指定数组的长度且数组长度通常是常量,在上面的例子中,我们的编译器是修改栈寄存器使得 char string[] 的是动态长度,而我们知道不能在栈上申请大块内存的,假如我们要在堆上得到一个动态的数组,并且同时记录数组的长度,这里就用到C语言一个相对特别的特性“零长度数组”。

我们先定义一个结构

typedef struct _record_t {	const char *name;	int int_value;	const char *string_value;}record_t;

然后再定义一个结构体

typedef struct _record_array_t {	size_t size;	record_t record[0];}record_array_t;

其中 record 就是 “零长度数组”。

在使用的时候,先声明一个 record_array_t 指针:

record_array_t *array;

为这个指针申请内存时是这样:

array = (record_array_t*)malloc( sizeof(record_array_t)+ sizeof(record_t)*num );

num 就是 record_t 结构体的个数。

本质上就是申请了一块内存,这个内存的空间刚好放下 record_array_t 这个结构再加上 num 个 record_t 的数据,而 record_array_t 中的成员 record 刚好就在 num 个 record_t 的“位置”上。

完整代码如下:

#include <stdio.h>#include <stdlib.h>typedef struct _record_t { const char *name; int int_value; const char *string_value;}record_t;typedef struct _record_array_t { size_t size; record_t record[0];}record_array_t;record_array_t * record_array_init(int num){ record_array_t *array; array = (record_array_t*)malloc( sizeof(record_array_t)+ sizeof(record_t)*num ); array->size = num;}int main (int argc,char *argv[]){ record_array_t *array = record_array_init(3); array->record[0].name = "AAA"; array->record[0].int_value = 111; array->record[0].string_value = "Test1"; array->record[1].name = "BBB"; array->record[1].int_value = 222;  array->record[1].string_value = "Test2"; array->record[2].name = "CCC"; array->record[2].int_value = 333; array->record[2].string_value = "Test3"; int i; for( i=0; i<array->size; i++ ){ printf("[%d]name[%s] int_value[%d] string_value[%s]\n",i, array->record[i].name, array->record[i].int_value, array->record[i].string_value); } free(array); return 0;}

编译执行输出结果:

[0]name[AAA] int_value[111] string_value[Test1][1]name[BBB] int_value[222] string_value[Test2][2]name[CCC] int_value[333] string_value[Test3]

虽然“零长度数组”这特性不是每个C编译器都支持,但现在大部分编译器都支持,包括主流的OS内核,因此可以放心使用。

小x全栈工程师原创

标签: #c语言长度 #c语言长度怎么算 #c语言memset #c语言测量数组的长度 #c语言长度为0的数组是什么