为什么在C中需要使用malloc来进行动态内存分配?

Why does malloc need to be used for dynamic memory allocation in C?

我一直在阅读 malloc 用于动态内存分配。但是如果下面的代码有效...

int main(void) {
        int i, n;
        printf("Enter the number of integers: ");
        scanf("%d", &n);

        // Dynamic allocation of memory?
        int int_arr[n];

        // Testing
        for (int i = 0; i < n; i++) {
                int_arr[i] = i * 10;
        }
        for (int i = 0; i < n; i++) {
                printf("%d ", int_arr[i]);
        }
        printf("\n");
}


... malloc 有什么意义?上面的代码不就是一种更简单易读的动态分配内存的方式吗?

我在 Stack Overflow 的另一个回答中读到,如果将某种标志设置为“迂腐”,那么上面的代码将产生编译错误。但这并不能真正解释为什么 malloc 可能是动态内存分配的更好解决方案。

查找 stackheap 的概念;不同类型的内存有很多微妙之处。函数内的局部变量位于 stack 中,并且仅存在于函数内。

在您的示例中,int_array 仅在其定义的函数执行尚未结束时存在,您无法在函数之间传递它。您不能 return int_array 并期望它起作用。

malloc() 用于创建存在于 上的内存块。 malloc return 指向此内存的指针。该指针可以作为变量(例如 returned)从函数中传递,并且可以在您的程序中的任何地方使用以访问您分配的内存块,直到您 free() 它。

示例:

'''C

int main(int argc, char **argv){
    int length = 10; 
    int *built_array = make_array(length); //malloc memory and pass heap pointer
    int *array = make_array_wrong(length); //will not work. Array in function was in stack and no longer exists when function has returned.

    built_array[3] = 5; //ok
    array[3] = 5; //bad 

    free(built_array) 

    return 0;  
 }

int *make_array(int length){
    int *my_pointer = malloc( length * sizeof int);
    //do some error checking for real implementation 
    return my_pointer; 
}

int *make_array_wrong(int length){
    int array[length]; 
    return array; 
 }

'''

注: 有很多方法可以完全避免使用 malloc,方法是在调用者中预先分配足够的内存等。建议用于嵌入式和安全关键程序,在这些程序中您希望确保永远不会 运行 内存不足。

int int_arr[n] 分配的内存仅保留到例程执行结束(当它 return 或以其他方式终止时,如 setjmp)。这意味着您不能按一个顺序分配东西并按另一个顺序释放它们。您不能分配临时工作缓冲区,在计算某些数据时使用它,然后为结果分配另一个缓冲区,然后释放临时工作缓冲区。要释放工作缓冲区,您必须从函数中 return,然后结果缓冲区将被释放到。

使用自动分配,您不能从文件中读取,为从文件中读取的每个事物分配记录,然后乱序删除一些记录。您根本无法动态控制分配的内存;自动分配被强制执行严格的后进先出 (LIFO) 顺序。

您不能编写分配内存、初始化它的子例程and/or进行其他计算,然后return将分配的内存分配给它们的调用者。

(有些人可能还会指出,通常用于自动对象的堆栈内存通常限制在 1-8 兆字节,而用于动态分配的内存通常要大得多。但是,这是所选设置的产物供共同使用,可以更改;它不是自动与动态分配的本质所固有的。)

当您的函数代码中包含以下内容时:

int int_arr[n];

表示你在函数栈上分配了space,一旦函数return这个栈将不复存在

想象一个您需要向调用者return数据结构的用例,例如:

Car* create_car(string model, string make)
{
    Car* new_car = malloc(sizeof(*car));
    ...
    return new_car;

}

现在,一旦函数完成,您仍然会有汽车对象,因为它是在堆上分配的。

仅仅因为某些东西 看起来 更漂亮并不能使它成为更好的选择。

VLA 有很多问题,其中最重要的是它们不足以替代堆分配的内存。

主要原因——也是最重要的——原因是 VLA 不是持久动态数据。也就是说,一旦您的函数终止,数据就会被回收(它存在于堆栈中的所有位置!),这意味着仍然挂在它上面的任何其他代码都是 SOL。

您的示例代码没有 运行 这个问题,因为您没有在本地上下文之外使用它。继续尝试使用 VLA 构建二叉树,然后添加一个节点,然后创建一个新树并尝试将它们都打印出来。

下一个问题是堆栈不是分配大量动态数据的合适位置 -- 它是用于函数帧的,函数帧的开头有限 space。全局内存池 OTOH 就是专门为这种用途设计和优化的。

提出问题并尝试理解事物是很好的。请注意,您不相信自己比许多人更聪明,他们利用现在将近 80 年的经验设计和实现了完全 运行 已知宇宙的系统。如此明显的缺陷在很久很久以前就会被立即识别出来,并且在我们任何一个出生之前就被消除了。

VLA 有它们的位置,可惜它很小。

声明局部变量会从堆栈中获取内存。这有两个后果。

  1. 那个内存一旦函数returns.
  2. 就被销毁了
  3. 堆栈内存有限,用于所有局部变量,以及函数return地址。如果分配大量内存,就会 运行 出问题。仅将其用于少量内存。

如果分配的内存很小,只在函数内部使用,malloc确实是不需要的。 如果内存量非常大(通常是 MB 或更多),上面的示例可能会导致堆栈溢出。 如果函数返回后内存还在使用,则需要malloc或者全局变量(静态分配)。

请注意,某些编译器可能不支持上述通过局部变量进行动态分配。