第五章 结构体与指针
- 以下划线来标识结构体
- 结构体的声明最好用
typedef
来重新定义,这样写代码时会很方便 - 直接使用结构体定义时,使用
.
来操作成员,如果使用动态定义方式,使用->
来操作成员。 - 使用
malloc
为结构体分配空间时,空间大小大于等于各成员大小之和。因为结构体所占空间大小并不是简单的将各成员大小相加,其中包含字节对齐等额外空间。 - 结构体各成员变量需要手动初始化和释放。
- 成员变量如果是动态分配初始化的,那么在释放时不能直接释放整个结构体,这时候成员变量所指向的堆区域还是有值的,直接释放整个结构体会造成内存泄漏。所以要先释放成员变量,然后再释放整个结构体。
- 把以上过程组合起来,就形成了构造函数和析构函数的原型。在C中,这两个过程需要手动处理,而且高级的面向对象语言中,这两个过程被自动执行。
结构体池
- 在大量需要结构体操作的时候,反复创建和释放会导致程序性能下降,为了优化创建释放过程,我们可以使用先创建一个结构体池的,需要时从池中获取,不需要时返回到池中。
使用以下方式可以定义一个结构体池
#define SIZE 10 Person *structPool[SIZE];
结构体池的大小
SIZE
是最需要考量的变量:值过大,那么池会占用大量的内存,浪费空间;值太小,程序很快就用完了池中资源,仍然会大量创建释放内存,效率仍然不理想。以下代码可以从池中获取结构体
Person *getStructPool() { int i; for (i = 0; i < SIZE; ++i) { if (structPool[i] != NULL) { Person *ptr = structPool[i]; structPool[i] = NULL; return ptr; } } Person *newPerson = (Person *)malloc(sizeof(Person)); return newPerson; }
我们通过轮询池数组找到第一个不为空的成员,然后将其返回,并将此位置设为空,腾出位置。如果池中资源都为空,那么只能重新配分一块空间。
以下代码可以将结构图存入池中
Person *setStructPool(Person *person) { int i; for (i = 0; i < SIZE; ++i) { if (structPool[i] == NULL) { structPool[i] = person; return person; } } deallocatePerson(person); free(person); return NULL; }
我们通过轮询池数组找到第一个空的成员,也就意味着此位置是空的,然后将结构体地址赋给数组元素,紧接着返回指针即可。如果池中资源都不为空,也就意味着池是满的,我们只能按正常方式将其释放。
注意: 存入池中资源后,返回了原指针,但是在外层我们不能将该指针释放,因为如果释放,那么池中的资源也会被释放。但是我们可以将该指针指向
NULL
。即该指针不可用。
- 对于清空结构体池,可以使用一下方式
void clearStructPool() { int i; for (i = 0; i < SIZE; ++i) if(structPool[i]!=NULL) { deallocatePerson(structPool[i]); free(structPool[i]); structPool[i] = NULL; } }
注意:
- 清空顺序,先释放每个结构体内存成员的空间,然后释放整个结构体的空间,再将结构体指针指向
NULL
。这时整个释放过程才是安全完整的- 可以使用工具
[Valgrind](http://valgrind.org/)
来检测程序是否存在内存泄漏问题。