美团笔试题解析:C语言中的柔性数组
开发C代码时,经常见到如下类型的结构体定义:
typedef struct list_t {
struct list_t *next;
struct list_t *prev;
char data[0];
} list_t;
最后一行char data[0]; 的作用是()。
A.方便管理内存缓冲区 B.减少内存碎片化
C.标识结构体结束 D.没有作用
该题目考查的是C语言中柔性数组的知识。 在C语言中,将结构中的最后一个成员定义为数组并将大小定义为0是一种特殊是用法,这种声明可以在C语言中巧妙地实现数组扩展。
在C99标准中,结构中的最后一个元素允许是未知大小的数组,称为柔性数组(flexible array)成员(也称为伸缩性数组成员),但结构中的柔性数组成员前面必须至少一个其他成员。柔性数组成员允许结构中包含一个大小可变的数组。柔性数组成员只作为一个符号地址存在,而且必须是结构体的最后一个成员,sizeof 返回的这种结构大小不包括柔性数组的内存。柔性数组成员不仅可以用于字符数组,还可以是元素为其它类型的数组。包含柔性数组成员的结构用malloc 函数进行内存的动态分配,且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。请看下面程序。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct list_t {
struct list_t *next;
struct list_t *prev;
char data[0];
} list_t;
int main(int argc, char*argv[])
{ char buf[] = "This is a string.", *p;
list_t *head;
printf("sizeof structlist_t = %d\n", sizeof( list_t ) );
head = ( list_t * )malloc( sizeof( list_t )+ sizeof( buf )+1 );
p = head->data;
printf("addr of list_tis %p. addr of data is %p.\n", head, p );
strcpy(head->data, buf);
printf("In struct sizeofdata = %d\n", sizeof( head->data ));
printf("In struct data =%s\n", head->data );
free( head );
return 0;
}
运行程序可能得到如下结果:
sizeof struct list_t = 8
addr of list_t is 003B1068.addr of data is 003B1070.
In struct sizeof data = 0
In struct data = This is astring.
由此可以看出,申请空间可以使用的语句是:
( list_t * )malloc( sizeof( list_t ) + 数组大小)
由于数组data没有元素,该数组在该结构分配时不占用空间,所以sizeof(struct list_t)=8。malloc申请的是26个字节的连续空间,返回一个指针指向这26个字节,强制转换成list_t *时,前面8个字节是list_t结构的两个成员next和prev,data的地址紧跟在结构之后。显然使用柔性数组可以按照实际需要只进行一次内存分配,从而减少了内存碎片。
请分析一下下面的程序,看一看如何使用柔性数组。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NUM 3
typedef struct list_t {
struct list_t *next;
struct list_t *prev;
char data[0]; // 柔性数组
} list_t;
int main(int argc, char* argv[])
{ char buf[][20] ={"This is a string.", "abc", "123456789"};
list_t * head, *p;
int i;
printf("sizeof list_t = %d\n", sizeof( list_t ) );
head = ( list_t * )malloc( sizeof( list_t ) );
head->next = NULL;
head->prev = NULL; // 可不使用
for ( i=0; i<NUM; i++ ) // 反向建立单链表
{ p = ( list_t * )malloc( sizeof( list_t)+strlen(buf[i])+1 );
strcpy( p->data, buf[i] );
printf("p=%p, p->data=%p, size *p=%d, size data=%d\n",
p,p->data, sizeof(*p), sizeof(p->data));
printf(" p-data=%s\n", p->data);
p->next = head->next;
head->next = p;
}
i = 1;
p = head->next;
while ( p!=NULL ) //输出链表中的结点
{printf("List %d: %p, %2d, %s\n", i++, p,strlen(p->data), p->data);
p = p->next;
}
while ( head!=NULL ) // 释放链表
{p = head->next;
free( head );
head = p;
}
return 0;
}
分析一下下面的程序,看一看在结构中使用指针该如何实现同样的功能。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NUM 3
typedef struct list_t {
struct list_t *next;
struct list_t *prev;
char * data; //使用指针
} list_t;
int main(int argc, char* argv[])
{ char buf[][20] ={"This is a string.", "abc", "123456789"};
list_t * head, *p;
int i;
printf("sizeof list_t = %d\n", sizeof( list_t ) );
head = ( list_t * )malloc( sizeof( list_t ) );
head->next = NULL;
head->prev =NULL; //可不使用
for ( i=0; i<NUM; i++) // 反向建立单链表
{ p = ( list_t * )malloc(sizeof( list_t ) );
p->data= ( char * )malloc( strlen(buf[i])+1 ); // 分别申请空间
strcpy( p->data, buf[i] );
printf("p=%p, p->data=%p, size *p=%d, size data=%d\n",
p,p->data, sizeof(*p), sizeof(p->data));
printf(" p-data=%s\n", p->data);
p->next = head->next;
head->next = p;
}
i = 1;
p = head->next;
while ( p!=NULL ) //输出链表中的结点
{ printf("List %d: %p,%2d, %s\n", i++, p, strlen(p->data), p->data);
p = p->next;
}
p = head->next;
while ( p!=NULL ) // 释放链表
{ list_t *q = p->next;
free( p->data ); // 分别释放空间
free( p );
p = q;
}
free( head );
return 0;
}
对照上面的两个程序可以看出,使用柔性数组可以减少内存碎片。所以答案是B。