我不需要 malloc() 这个数组吗?

Don't I have to malloc() this array?

我正在努力学习 malloc() / free() 的 C 语言规则。考虑下面的代码,它运行得很好。 (在 Linux 上,使用 GCC 编译。)具体来说,我想知道 arr 数组。我知道你必须为数组 中的所有结构元素分配 malloc... 但为什么你不必为数组分配 malloc 本身?

#include<stdio.h>
#include<stdlib.h>
#define ARR_SIZE 100

typedef struct containerStruct{
    int data1;
    int data2;
    int data3;
    // ...etc...
} container;

int main(){

    container* arr[ARR_SIZE];       // Don't I have to malloc this?

    int i;
    for(i=0; i<ARR_SIZE; i++){
            arr[i] = (container*) malloc (sizeof(container));   // I get why I have to malloc() here
    }

    ...do stuff...

    for(i=0; i<ARR_SIZE; i++){
            free(arr[i]);        // I get why I have to free() here
    }

    // Don't I have to free(arr) ?
    return 0;
}

我猜 container* arr[ARR_SIZE]; 行告诉编译器它需要知道的所有信息,以便在内存中为数组划分出 space,这就是这段代码有效的原因。

但不知何故,它仍然 "feel" 不对。当我 malloc() 某些东西时,我在堆上保留内存 space ,对吧?但是我的 container* arr[ARR_SIZE]; 调用在堆栈上创建了数组。所以......容器*搬运工数组存在于堆栈中,所有这些容器*指针都指向堆上的容器结构。对吗?

container* arr[ARR_SIZE]; 告诉 编译器 分配 ARR_SIZE 类型 container* 元素的数组] 并且编译器相应地分配内存。

换句话说,这类似于说 int x[5] = 0;,其中编译器为 5 int 的数组分配了足够的 space。在您的情况下,编译器为 ARR_SIZE 个指针分配了足够的 space,container* 就是这样。现在,由您决定让这些指针 指向 有效的内存位置。为此,您可以

  • 使用内存分配器函数(如您所述,它从 分配内存)
  • 分配相同类型的其他变量的地址(无论如何不需要从堆中分配)。

所以,最重要的是,您不需要为 数组分配任何内存。对于每个单独的数组元素,您需要使用内存分配器函数分配内存,因为您希望每个元素指向 valid 内存。

在您的 arr 声明下方,您有以下内容:

int i;

其中保留了足够 space (可能在 "stack" 上) 来存储一个 int。这 "feel" 你也觉得不对吗?

arr的声明没有什么不同。编译器为 container *ARR_SIZE 元素数组分配足够的 space (可能在 "stack")

您可以选择以下选项之一:


int i;
container** arr = malloc(sizeof(container*)*ARR_SIZE);
for (i=0; i<ARR_SIZE; i++)
    arr[i] = malloc(sizeof(container));
// do stuff...
for(i=0; i<ARR_SIZE; i++)
    free(arr[i]);
free(arr);

int i;
container* arr[ARR_SIZE];
for (i=0; i<ARR_SIZE; i++)
    arr[i] = malloc(sizeof(container));
// do stuff...
for(i=0; i<ARR_SIZE; i++)
        free(arr[i]);

container arr[ARR_SIZE];
// do stuff...

由于ARR_SIZE是常数,你不妨选择最后一个选项。

由于 ARR_SIZE 是动态数组 *arr[ARR_SIZE] 的固定成员,您不再需要为整个数组分配内存,而只需为其中的元素分配内存,因为它们在这种情况下是灵活的.

当你malloc any sort of dynamic array, it is safe to always check the return value of the void* pointer returned from malloc. Additionally, when you free每个元素的时候,再把每个成员都设置成NULL就更安全了,防止指针再次访问内存。

通过这段代码说明:

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

#define ARR_SIZE 100

typedef struct {
    int data1;
    int data2;
    int data3;
    // ...etc...
} container_t;

int
main(void) {
    container_t *arr[ARR_SIZE];
    int i;

    for (i = 0; i < ARR_SIZE; i++) {
        arr[i] = malloc(sizeof(container_t));
        if (arr[i] == NULL) {
            printf("Malloc Problem here\n");
            exit(EXIT_FAILURE);
        }
    }

    for (i = 0; i < ARR_SIZE; i++) {
        if (arr[i]) {
            free(arr[i]);
            arr[i] = NULL;
        }
    }

    return 0;
}

When I malloc() something, I'm reserving memory space on the heap, right?

别说了,堆是一个实现细节。您正在动态分配内存。您还负责再次动态释放它。

But my container* arr[ARR_SIZE]; call

这不是调用,而是声明。

creates the array on the stack.

也别说了,栈也是一个实现细节

您正在声明一个具有自动作用域的局部变量(这里是一个指针数组),编译器负责管理它的内存和生命周期。

它的生命周期不是动态的,因为一旦到达封闭块末尾的 },它就变得无法访问,因此编译器可以确定地为您处理它。

所有局部变量在此处的行为相同:

{
  int i;
  double d[2];
} /* neither i nor d are reachable after here,
     so the compiler takes care of releasing any storage */

So... the array of container* pointers exists on the stack,

考虑更简单的声明 container c;。这是一个具有自动作用域的局部变量,编译器会按照讨论的方式处理(解除)分配。

现在考虑 container *p;。这是也是一个具有自动作用域的局部变量,但该变量是一个指针。您仍然需要手动将其指向 ,如果您指向的对象是从 malloc 返回的,则您需要自己 free

进一步考虑一个简单的数组container a[2];。现在你有一个本地 array 具有自动范围,包含你的 container 类型的两个实例。这两个实例具有相同的生命周期,它仍然由编译器管理。您可以访问 a[0]a[1],几乎所有其他内容都是非法的。

and all of those container* pointers point to a container struct on the heap. Is that correct?

没有。最后考虑container* ap[2]。同样,我们有一个具有自动作用域的本地数组,包含两个 指针 。指针的类型是 pointer-to-containerpointers 的生命周期由编译器管理。然而,指针还没有指向任何东西,你有责任决定将它们指向什么,并弄清楚指向的生命周期-在事情有。

一旦你这样做了

ap[i] = malloc(sizeof(*ap[i]));

(你不需要转换,避免显式命名类型通常更安全,以防你稍后更改它并分配错误的大小),你已经分配了一个你负责的对象释放。您还将数组中的一个指针指向该对象,但这并不会以某种方式改变指针本身的生命周期。该数组像往常一样超出范围无论它的指针指向什么地方。