非动态数组何时在结构中释放

When do nondynamic arrays get free'd in a struct

我有以下struct

typedef struct  {
    int id;
    double c[2];
    double p[4][2];
} Detection;

这是它的构造函数

Detection* create_detection(int i) {
    Detection* detection = (Detection*) malloc(sizeof(Detection));
    detection->id = i;
    return detection;
}

如您所见,结构本身是动态分配的。我正在尝试为它编写一个析构函数,这就是我目前所拥有的。

void destroy_detection(Detection* detection) {
    free(detection);
}

这个 cp 也会免费吗?

Will this free c and p as well?

.

数组 cp 是静态分配的,这意味着 会为您管理内存;你不用担心。


经验法则:调用 free() 的次数与调用 malloc() 的次数相同。

在这里您动态分配了一次内存,因此您必须只取消分配一次动态内存。

这里只有一个分配。 cp 字段没有单独分配。一般规则是对 malloc 的每次调用都必须通过对 free 的调用进行一对一的平衡。所需要的就是所写的析构函数。如果结构内还有其他动态分配的指针,则可能需要额外的 free 调用。

请注意,cp 字段具有固定大小,包含在 sizeof(Detection) 中。在 C++ 中,字段可以有自己的构造函数,可以进行动态分配,但它们通常也会从编译器在父析构函数中生成的代码中自动析构。

C 有可变长度数组 (VLA),但它们不能在结构中声明,只能在函数参数列表或函数内部的块中声明。

cp 的内存是在结构中内联分配的。这是因为 cp 作为数组而不是指针。如果在 C 中指针和数组不是一回事。如果将 c 和 p 定义为指针,那么您必须在使用它们之前为它们分配和释放内存。

造成混淆的原因是数组有时会退化为指针,反之亦然。例如 double* ptr = cdouble* ptr = &c[0] 等价(c 是一个数组)。指针衰减在访问数组时发生,而在我们定义数组时不会发生。

我想明确说明其他答案中隐含的重要部分:

malloc不知道分配内存是为了什么。

它接收字节数作为输入和 returns 一个指针,它后面至少有那么多字节可寻址。

我的 compiler/machine 组合 sizeof(Detection)88。所以 malloc 只是在堆中分配了 88 个字节,returns 分配了一个指向它的指针。 程序员有责任以某种方式解释该内存(编译器有责任帮助以正确的方式在内存中布局结构成员,并生成适当的代码,给出内存指针和成员名称,它将计算内部的适当偏移量该内存访问该成员数据) - 在您的代码中,您将该地址分配给指向 Detection 结构的指针。您也可以尝试将其分配给指向另一个结构的指针,它可能会或可能不会工作(因为程序崩溃 and/or 会擦除计算机上的所有数据),具体取决于结构大小和分配的内存量。

因此,malloc 不知道它为包含数组的结构分配内存。它只是分配连续的内存区域,如果编码正确(即传递给 malloc 的适当大小)就足以包含 Detection.

类型的结构

我们可以稍微修改一下您的代码(我作弊了一点,因为在我的机器上大小 long longdouble 碰巧是一样的,不要那样做):

#include <stdlib.h>
#include <stdio.h>

typedef struct {
    int id;
    double c[2];
    double p[4][2];
} Detection;

int main()
{
    size_t size = sizeof(Detection);
    printf("sizeof(Detection) = %u\n", size);
    Detection *detection = malloc(size);
    detection->id = 0x01020304;
    *(long long *)(&detection->c[0]) = 0x1011121314151617;
    *(long long *)(&detection->c[1]) = 0x18191A1B1C1D1E1F;
    *(long long *)(&detection->p[0][0]) = 0x2021222324252627;
    *(long long *)(&detection->p[0][1]) = 0x28292A2B2C2D2E2F;
    *(long long *)(&detection->p[1][0]) = 0x3031323334353637;
    *(long long *)(&detection->p[1][1]) = 0x38393A3B3C3D3E3F;

    *(long long *)(&detection->p[2][0]) = 0x4041424344454647;
    *(long long *)(&detection->p[2][1]) = 0x48494A4B4C4D4E4F;
    *(long long *)(&detection->p[3][0]) = 0x5051525354555657;
    *(long long *)(&detection->p[3][1]) = 0x58595A5B5C5D5E5F;
}

然后运行它在调试器中查看内存布局,在我的例子中是(从第三行开始的0x01097018地址):

0x01097008  01 00 00 00 58 00 00 00 4e 00 00 00 fd fd fd fd  ....X...N...ýýýý
======== detection starts here =========
0x01097018  04 03 02 01 cd cd cd cd 17 16 15 14 13 12 11 10  ....ÍÍÍÍ........
0x01097028  1f 1e 1d 1c 1b 1a 19 18 27 26 25 24 23 22 21 20  ........'&%$#"! 
0x01097038  2f 2e 2d 2c 2b 2a 29 28 37 36 35 34 33 32 31 30  /.-,+*)(76543210
0x01097048  3f 3e 3d 3c 3b 3a 39 38 47 46 45 44 43 42 41 40  ?>=<;:98GFEDCBA@
0x01097058  4f 4e 4d 4c 4b 4a 49 48 57 56 55 54 53 52 51 50  ONMLKJIHWVUTSRQP
0x01097068  5f 5e 5d 5c 5b 5a 59 58

从内存中的值可以很容易地看到结构在内存中的布局,可以注意到数组嵌入到内存布局中。

因此,free 也完全不知道传递给它的指针的另一端是什么类型的结构。它只是接收一个地址,该地址[据称]先前由mallocvoid *)返回,并且知道如何将其作为内存块(而不是某种类型的结构)释放。除了将内存标记为 "not used".

之外,没有任何 "destruction" 发生

我在 malloc 返回的地址之前添加了额外的 16 字节内存,以演示 free 如何知道要释放多少内存(顺便说一下,这完全取决于实现,你不应该以任何方式依赖它)。您可能会注意到第一行中的 58 转换为十进制 88 - 它是分配的内存大小

可以轻松地将 size + 42 传递给 malloc,它仍然是正确的程序,只是会浪费内存。