变长数组
C语言在C99以前,数组的维度必须是整数常量表达式, 而C99则做了很大改进,允许数组维度为整形变量或者整形表达式(关键点运行时才能确定)。这种数组称为(variable-length array),简称VLA,中文一般称为变长数组。
例如:
int n;
scanf("%d", &n);
int array[n];
以上这种写法在C99 以前是不会通过编译的。那么VLA是怎么实现的呢?
看看下面的柔性数组或许有些启发。
柔性数组
例如要在结构体中存放一个长度不固定的字符串,可以采用下面这种方式。
#include <stdio.h>
#include <malloc.h>
#include <string.h>
typedef struct line
{
int len;
char *contents;
}line;
int main(int argc, char **argv)
{
char str[] = "hello world";
struct line *ptr = (struct line*)malloc(sizeof(line) + strlen(str) + 1);
ptr->len = strlen(str);
strcpy((char*)(ptr + 1), str);
/*
printf("start: %p\n\n", (char*)ptr);
printf("(char*)(ptr+1)address: %p\n", (char*)(ptr+1));
printf("(char*)(ptr+1): %s\n\n", (char*)(ptr+1));
printf("&(ptr->contents): %p\n", &(ptr->contents));
printf("ptr->contents: %s\n\n", ptr->contents);
printf("sizeof(int): %d\n", sizeof(int));
printf("sizeof(line): %d\n", sizeof(line));
*/
}
/*
*注释掉的代码用于查看内存地址,方便理解,顺便可以发现内存对齐现象。
*编译: Linux gcc.
*/
但这样处理等同于浪费掉了*contents, 且取用字符串时, 必须用(char*)(ptr+1)的方式, 那么有没有可以既可以不浪费掉*contents, 又可以直接用ptr->contents的方式取字符串呢?那么就要靠下面展示的柔性数组(零长数组)了。
#include <stdio.h>
#include <malloc.h>
#include <string.h>
typedef struct line
{
int len;
char contents[0]; // 修改的地方。
}line;
int main(int argc, char **argv)
{
char str[] = "hello world";
struct line *ptr = (struct line*)malloc(sizeof(line) + strlen(str) + 1);
ptr->len = strlen(str);
strcpy((char*)(ptr + 1), str);
/*
printf("start: %p\n\n", (char*)ptr);
printf("(char*)(ptr+1)address: %p\n", (char*)(ptr+1));
printf("(char*)(ptr+1): %s\n\n", (char*)(ptr+1));
printf("&(ptr->contents): %p\n", &(ptr->contents));
printf("ptr->contents: %s\n\n", ptr->contents);
printf("sizeof(int): %d\n", sizeof(int));
printf("sizeof(line): %d\n", sizeof(line));
*/
}
这样做的巧妙之处,数组长度为0的contents并不会占用内存空间,这个非对象符号仅仅代表一个地址而已。但C语言标准库不允许定义长度为0的数组,能否实现要靠编译器是否有扩展该功能。
所以按照柔性数组我们可以猜测一下VLA大概实现(仅仅是猜测, 我也不知道对不对):
#include <stdio.h>
#include <malloc.h>
typedef struct Array{
int data[0];
}Array;
int main(int argc, char **argv)
{
int n;
scanf("%d", &n);
Array *p = (Array*)malloc(sizeof(int) * n);
((int*)p)[0] = 10;
((int*)p)[1] = 20;
((int*)p)[2] = 30;
printf("n=%d, %d, %d, %d\n", n, ((int*)p)[0], ((int*)p)[1], ((int*)p)[2]);
}
好吧,VLA和柔性数组大概就介绍这么多,才疏学浅,如有疏漏,望指正。