何时以及为什么要使用malloc

96

嗯,我不明白何时以及为什么需要使用malloc来分配内存。

这是我的代码:

#include <stdlib.h>

int main(int argc, const char *argv[]) {

  typedef struct {
    char *name;
    char *sex;
    int age;
  } student;


  // Now I can do two things
  student p;

  // Or
  student *ptr = (student *)malloc(sizeof(student));

  return 0;
}

如果我可以直接使用student p;,为什么需要分配内存呢?


3
你需要了解堆和栈内存之间的差异,请看这个问题:https://dev59.com/hHVD5IYBdhLWcg3wHn2d - jlledom
5
在使用malloc/calloc等函数分配内存后,请不要忘记调用free释放内存。 - jotik
6个回答

96

malloc 用于动态内存分配。所谓动态分配,就是在程序运行期间进行内存分配。例如,在编译时不知道内存量的情况下。

我们来看一个例子。假设你知道最多有20个学生。那么你可以创建一个具有静态20个元素的数组。这个数组可以容纳最多20个学生。但是,如果你不知道学生人数呢?假设第一个输入是学生人数。它可能是10、20、50或其他任何数字。现在你需要在运行时将输入n(学生数量)作为参数,并使用malloc 动态地分配相应量的内存。

这只是其中一种情况。还有许多类似的情况需要使用动态分配。

请查看手册页malloc(3)


8
仅仅因为在编译时不知道需要多少内存,并不意味着必须涉及堆。 - Matt Joiner
6
@Matt Joiner,那只是一个例子。如果你认为这值得被踩,那我也没什么可说的了。 - taskinoor
@Matt Joiner,我稍微编辑了一下答案。现在可能听起来更好了。 - taskinoor
17
这个答案没有提到使用 malloc 分配内存的非常重要的用途,即分配具有超出当前块执行生命周期的内存。 - Eric Postpischil

52

当您需要分配对象超出当前块的执行生命周期(在这种情况下,复制返回将非常昂贵),或者需要分配大于该堆栈大小的内存时(即,一个3 MB本地堆栈数组是一个糟糕的想法)时,您可以使用malloc

C99引入VLAs之前,您还需要使用它来执行动态大小数组的分配。但是,它需要用于创建动态数据结构,如treeslistsqueues,这些数据结构被许多系统使用。可能还有很多原因; 这些只是其中几个。


词法作用域和存储期是两个不同的概念。一个对象可以具有块级作用域和静态存储期。 - ouah
@ouah:那它就不会是一个栈分配的对象,这正是我所指的。 - Necrolis
在 C99 之前,您可以使用 alloca。除此之外,这是最正确的答案,应该标记为解决方案。 - Matt Joiner

21

稍微扩展一下这个例子的结构,考虑如下情况:

#include <stdio.h>

int main(int argc, const char *argv[]) {

    typedef struct {
        char *name;
        char *sex;
        char *insurance;
        int age;
        int yearInSchool;
        float tuitionDue;
    } student;

    // Now I can do two things
    student p;

    // Or
    student *p = malloc(sizeof *p);
}

C是一种按值传递的语言,而不是按引用传递的。在这个例子中,如果我们将'p'传递给某个函数进行处理,我们将创建整个结构的副本。这会使用额外的内存(该特定结构所需的总空间),速度较慢,并且可能不会很好地扩展(稍后会详细说明)。然而,通过传递*p,我们并没有传递整个结构,而只是传递了一个指向该结构的内存地址。传递的数据量更小(指针的大小),因此操作速度更快。

现在,知道了这一点,想象一下一个要创建和管理成千上万个记录的程序(如学生信息系统)。如果您通过值传递整个结构,在一组数据上操作将比只传递每个记录的指针需要更长的时间。


3
你不能只使用 &p 来传递函数指针,而不使用 malloc 吗? - Nitin Savant

12

让我们从不同的角度来解决这个问题。

大小

malloc允许你分配比使用student p;或者int x[n];简单分配的内存更大的空间。原因在于malloc在堆上分配空间,而另外两种方式是在栈上分配空间。

C编程语言静态、自动或动态管理内存。静态变量在主存中分配,通常与程序的可执行代码一起分配,并持续存在于整个程序的生命周期内;自动变量分配在栈上,在函数被调用和返回时出现和消失。对于静态变量和自动变量,分配的大小必须是编译时常量(除了变长自动数组的情况[5])。如果所需大小直到运行时才知道(例如,如果正在从用户或磁盘文件读取任意大小的数据),则使用固定大小的数据对象是不足够的。(来源:维基百科)

作用域

通常,声明的变量会在它所在的块后被删除/释放(它们在栈上声明)。另一方面,使用malloc分配内存的变量将一直保留,直到它们被手动释放。

这也意味着在函数内创建变量、数组或结构体并返回它们的地址是不可能的(因为指向的内存可能会被释放)。编译器还试图通过以下警告提醒您:

警告 - 返回与局部变量'matches'关联的堆栈内存的地址

更多详情,请阅读此内容

改变大小(realloc

正如您可能猜到的那样,使用常规方法是不可能的。

错误检测

如果无法分配内存:常规方式可能会导致程序终止,而malloc将返回一个可以在程序中轻松捕获和处理的NULL

将来更改字符串内容

如果您创建存储字符串的变量,例如char *some_memory = "Hello World";,则不能执行some_memory[0] = 'h';,因为它存储为字符串常量,而其中存储的内存是只读的。如果改用malloc,则稍后可以更改其内容。 有关更多信息,请查看此答案

有关可变大小数组的更多详细信息,请查看此内容


“正常的方式”是什么?你应该在这里更具体。例如,您无法调整静态或本地变量的大小。 - jwdonahue
如果变量在另一个作用域中不存在了,你该如何调用它呢? - Carlitos_30

6

malloc = 内存分配

如果您了解其他编程语言,可能已经使用过 new 关键字。

在 C 语言中,malloc 执行的正是同样的操作。它接收一个参数,即要分配的内存大小,并返回一个指针变量,该变量指向整个内存块中的第一个内存块,这是您在内存中创建的。例如:

int *p = malloc(sizeof(*p)*10);

现在,*p将指向连续10个整数块在内存中保留的第一个块。
您可以使用++--运算符遍历每个块。

4
你的意思是连续的10个整数块吗? - i_use_the_internet

-2
在这个例子中,它似乎确实没有什么用处。
但是现在想象一下,当你使用套接字或文件I/O并且必须从可变长度读取数据包时,你只能在运行时确定。或者当使用套接字并且每个客户端连接需要服务器上的一些存储空间时。你可以创建一个静态数组,但这会给你一个客户端限制,这将在编译时确定。

1
可变长度数组可以很好地存储在堆栈上。使用堆并不是使用可变长度数组的理由。 - Matt Joiner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接