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

时间:2016-11-16 14:47:15

标签: c arrays struct malloc dynamic-memory-allocation

我正在努力学习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];行告诉编译器需要知道如何在内存中为数组划分空间,这就是为什么这段代码可以工作。

但它仍然没有“感觉”正确,不知何故。当我malloc()的时候,我正在堆上保留内存空间,对吧?但我的container* arr[ARR_SIZE];调用会在堆栈上创建数组。所以...容器*搬运工的数组存在于堆栈中,所有这些容器*指针指向堆上的容器结构。这是对的吗?

5 个答案:

答案 0 :(得分:4)

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

int i;

保留足够的空间(可能在“堆栈”上)来存储int。这对你“感觉”有什么不对吗?

arr的声明也不例外。编译器为ARR_SIZE的{​​{1}}元素数组分配了足够的空间(可能在“堆栈”上)

答案 1 :(得分:4)

  

当我使用malloc()时,我正在保留堆上的内存空间,对吗?

不要说,堆是一个实现细节。你是动态分配内存。您还有责任再次动态释放它。

  

但是我的容器* arr [ARR_SIZE];调用

这不是电话,而是宣言。

  

在堆栈上创建数组。

也不要说,堆栈也是一个实现细节。

你声明一个带有自动范围的本地(这里是一个指针数组),编译器负责管理它的内存和生命周期。

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

所有局部变量的行为都相同:

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

所以...容器*指针数组存在于堆栈中,

考虑更简单的声明container c;。这是具有自动范围的本地,编译器负责(de)分配,如上所述。

现在考虑container *p;。这也是 一个具有自动范围的局部变量,但该变量是一个指针。您仍然需要手动将指向某些内容,如果您指向的内容是从malloc返回的,则您需要自己free

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

  

并且所有这些容器*指针指向堆上的容器结构。这是对的吗?

没有。最后考虑container* ap[2]。我们再次拥有一个具有自动范围的本地数组,其中包含两个指针。指针的类型是指向容器的指针指针的生命周期由编译器管理。但是,指针在任何地方都没有指向,并且你有责任决定将它们指向什么,并找出指向的生命周期事情有。

一旦你做了

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

(你不需要强制转换,并且通常更安全地避免明确命名类型,以防你以后更改它并分配错误的大小),你已经分配了一个你负责释放的对象。你还在这个对象中指出了数组中的一个指针,但这并没有以某种方式改变指针本身的生命周期。该数组就像通常的 指针一样超出范围。

答案 2 :(得分:3)

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

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是常量,您也可以选择最后一个选项。

答案 3 :(得分:2)

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

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

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

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

答案 4 :(得分:2)

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

当您malloc任何类型的动态数组时,始终检查从void*返回的malloc指针的返回值是安全的。此外,当您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;
}