变长结构体
struct test
{
int nSize;
char data[]; // 或者 char data[0];但建议使用 char data[]; 注意:c98 时不支持柔性数组,其仅作为非标准扩展。到c99时纳入标准
};
如上面代码即为一变长结构体,其中 char data[]; 为一变长数组,称之为柔性数组。正是因其为变长数组,故结构体才可变长。使用 test 结构体时,可用 malloc 申请大于 sizeof(test) 长度的空间。如下:
const auto nSizeTest = sizeof(test);
const auto nExtraSize = * (sizeof(char)); test* stpTest = (test*)malloc(nSizeTest + nExtraSize); stpTest->nSize = nSizeTest + nExtraSize;
memset((void*)stpTest->data, 0x0L, nExtraSize);
const auto nTempLen = strlen(stpTest->data);
for (auto nIndex = ; nIndex < nExtraSize - ; ++nIndex) {
stpTest->data[nIndex] = char( + nIndex);
}
stpTest->data[nExtraSize - ] = '\0'; // some code here........ free(stpTest); // 此处 free 的是刚才所申请的全部内存空间
stpTest = nullptr;
使用柔性数组有以下几个好处:
- 首先柔性数组不占内存,值代表地址;
- 可以通过stpTest->data来访问字符串,符合常规用法。
- 字符串长度为动态分配。
关于柔性数组的注意点:
- 柔性数组只能放置于结构体的末尾声明
- 由于柔性数组是动态可变长的,则一般情况下只会用在没有继承关系、没有虚表的变长结构体(或类中)中,如果有继承关系的或虚表,则后果将非常严重。(有看过对象模型的人,肯定清楚为什么,此处就不多说)
- 上面变长结构体中的柔性数组除了 char 型别外,还可以是其他任何类型,甚至是自定义的类类型。但是:如果是自定义的类类型,则需要自己手动调用构造与析构。因为 malloc 与 free 时,不会自动调用构造与析构。从而可能导致不可预知的结果,以及内存泄漏的可能。比如:
class obj
{
public:
int nIndex;
~obj() {
std::cout << "obj " << nIndex << " destroy" << std::endl;
}
}; struct test
{
int nSize;
obj data[];
}; int _tmain(int argc, _TCHAR* argv[])
{ const auto nSizeTest = sizeof(test);
const auto nExtraSize = * (sizeof(obj)); test* stpTest = (test*)malloc(nSizeTest + nExtraSize); stpTest->nSize = nSizeTest + nExtraSize;
memset((void*)stpTest->data, 0x0L, nExtraSize);
for (auto nIndex = ; nIndex < ; ++nIndex) {
stpTest->data[nIndex].nIndex = nIndex + ;
} auto nSize = sizeof(test);
std::cout << "sizeof(test) = " << nSize << std::endl; // 此处输出 4.说明柔性数组并不会占用 test 的空间. nSize = sizeof(*stpTest);
std::cout << "sizeof(*stpTest) = " << nSize << std::endl; // 此处输出 4 obj* ptr = stpTest->data; free(stpTest); // 此处将释放前面所申请的全部空间,但是:不会调用 ~obj() 函数!!!
stpTest = nullptr; //ptr->nIndex = 777; // 如果调用此句会蹦,因为前面 free 时已经全部释放掉所申请空间了,包括柔性数组 data 的那3个 obj 对象的空间 system("pause"); return ;
}
参考文献: