重复分配然后释放结构体会产生一些开销,可能导致巨大的性能瓶颈。解决这个问题的一种办法是为分配的结构体单独维护一个表。当用户不再需要某个结构体实例时,将其返回结构体池中。当我们需要某个实例时,从结构体池中获取一个对象。如果池中没有可用的元素,我们就动态分配一个实例。这种方法高效地维护一个结构体池,能按需使用和重复使用内存。
为了说明这种方法,我们会用之前定义的Person
结构体。用数组维护结构体池,也可以用链表等更复杂的表,6.4.1节有相关说明。为了让示例简单,我们用了指针数组,声明如下:
#define LIST_SIZE 10
Person *list[LIST_SIZE];
使用表之前需要先初始化。下面的函数为数组每个元素赋值NULL
:
void initializeList() {
for(int i=0; i<LIST_SIZE; i++) {
list[i] = NULL;
}
}
我们用两个函数来添加和获取结构体。第一个是getPerson
函数,如下所示。如果存在可用的结构体,这个函数从表中获取一个。将数组的元素跟NULL
比较,返回第一个非空的元素,然后将它在list
中的位置赋值为NULL
。如果没有可用的结构体,那就创建并返回一个新的Person
实例。这样就避免了每次需要结构体时都动态分配内存的开销,我们只在池中为空时才分配内存。返回实例的初始化可以在返回之前就做好,也可以由调用者来做,取决于应用程序的需要。
Person *getPerson() {
for(int i=0; i<LIST_SIZE; i++) {
if(list[i] != NULL) {
Person *ptr = list[i];
list[i] = NULL;
return ptr;
}
}
Person *person = (Person*)malloc(sizeof(Person));
return person;
}
第二个函数是returnPerson
,这个函数要么将结构体返回表,要么把结构体释放掉。我们会检查数组元素看看有没有NULL
值,有的话就将person
添加到那个位置,然后返回指针。如果表满了,就用deallocatePerson
函数释放person
内的指针,然后释放person
,最后返回NULL
。
Person *returnPerson(Person *person) {
for(int i=0; i<LIST_SIZE; i++) {
if(list[i] == NULL) {
list[i] = person;
return person;
}
}
deallocatePerson(person);
free(person);
return NULL;
}
下面的代码说明了表的初始化,以及如何将一个结构体添加到表中:
initializeList();
Person *ptrPerson;
ptrPerson = getPerson();
initializePerson(ptrPerson,"Ralph","Fitsgerald","Mr.",35);
displayPerson(*ptrPerson);
returnPerson(ptrPerson);
这种方法有个问题,就是表的长度。如果表太短,那么就需要更频繁地分配并释放内存。如果表太长而没有使用结构体,那么就会浪费大量的内存,也不能用在其他地方。可以用更复杂的表管理策略来管理表的长度。