为什么在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 可能是动态内存分配的更好解决方案。
查找 stack
和 heap
的概念;不同类型的内存有很多微妙之处。函数内的局部变量位于 stack
中,并且仅存在于函数内。
在您的示例中,int_array
仅在其定义的函数执行尚未结束时存在,您无法在函数之间传递它。您不能 return int_array
并期望它起作用。
malloc()
用于创建存在于 堆 上的内存块。 malloc
return 指向此内存的指针。该指针可以作为变量(例如 return
ed)从函数中传递,并且可以在您的程序中的任何地方使用以访问您分配的内存块,直到您 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 有它们的位置,可惜它很小。
声明局部变量会从堆栈中获取内存。这有两个后果。
- 那个内存一旦函数returns.
就被销毁了
- 堆栈内存有限,用于所有局部变量,以及函数return地址。如果分配大量内存,就会 运行 出问题。仅将其用于少量内存。
如果分配的内存很小,只在函数内部使用,malloc确实是不需要的。
如果内存量非常大(通常是 MB 或更多),上面的示例可能会导致堆栈溢出。
如果函数返回后内存还在使用,则需要malloc或者全局变量(静态分配)。
请注意,某些编译器可能不支持上述通过局部变量进行动态分配。
我一直在阅读 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 可能是动态内存分配的更好解决方案。
查找 stack
和 heap
的概念;不同类型的内存有很多微妙之处。函数内的局部变量位于 stack
中,并且仅存在于函数内。
在您的示例中,int_array
仅在其定义的函数执行尚未结束时存在,您无法在函数之间传递它。您不能 return int_array
并期望它起作用。
malloc()
用于创建存在于 堆 上的内存块。 malloc
return 指向此内存的指针。该指针可以作为变量(例如 return
ed)从函数中传递,并且可以在您的程序中的任何地方使用以访问您分配的内存块,直到您 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 有它们的位置,可惜它很小。
声明局部变量会从堆栈中获取内存。这有两个后果。
- 那个内存一旦函数returns. 就被销毁了
- 堆栈内存有限,用于所有局部变量,以及函数return地址。如果分配大量内存,就会 运行 出问题。仅将其用于少量内存。
如果分配的内存很小,只在函数内部使用,malloc确实是不需要的。 如果内存量非常大(通常是 MB 或更多),上面的示例可能会导致堆栈溢出。 如果函数返回后内存还在使用,则需要malloc或者全局变量(静态分配)。
请注意,某些编译器可能不支持上述通过局部变量进行动态分配。