什么时候应该在C中使用malloc,什么时候不应该?


94

我了解malloc()的工作方式。我的问题是,我将看到以下内容:

#define A_MEGABYTE (1024 * 1024)

char *some_memory;
size_t size_to_allocate = A_MEGABYTE;
some_memory = (char *)malloc(size_to_allocate);
sprintf(some_memory, "Hello World");
printf("%s\n", some_memory);
free(some_memory);

为了简洁起见,我省略了错误检查。我的问题是,您不能通过初始化指向内存中某些静态存储的指针来完成上述操作吗?也许:

char *some_memory = "Hello World";

在什么时候您实际上需要自己分配内存,而不是声明/初始化需要保留的值?


5
回复:为了简洁起见,我省略了错误检查 -不幸的是,太多的程序员忽略了错误检查,因为他们没有意识到malloc()可能会失败!
安德鲁(Andrew)

Answers:


132
char *some_memory = "Hello World";

正在创建一个指向字符串常量的指针。这意味着字符串“ Hello World”将位于内存的只读部分中,而您只有一个指向它的指针。您可以将字符串用作只读。您无法对其进行更改。例:

some_memory[0] = 'h';

正在自找麻烦。

另一方面

some_memory = (char *)malloc(size_to_allocate);

正在分配一个char数组(一个变量),并且some_memory指向已分配的内存。现在,该数组可以读写。您现在可以执行以下操作:

some_memory[0] = 'h';

并且数组内容更改为“ hello World”


19
为了澄清,就我所希望的(我确实给了+1),您可以不使用malloc()而执行相同的操作,只需使用字符数组即可。类似的东西:char some_memory [] =“ Hello”; some_memory [0] ='W'; 也可以。
randombits

19
你说的没错。你可以做到的。当您使用malloc()时,内存将在运行时动态分配,因此您无需在编译时固定数组大小,也可以使用realloc()使其增大或缩小,而在执行这些操作时,这些操作都无法完成:char some_memory [] =“你好”; 在这里,即使您可以更改数组的内容,其大小也是固定的。因此,根据您的需要,可以使用以下三个选项之一:1)指向char const的指针2)动态分配的数组3)固定大小,编译时分配的数组。
codaddict

为了强调它是只读的,您应该编写标准const char *s = "hi";是否真正要求这样做?
Till Theis

@till,否,因为您声明了一个初始化为字符串文字“ hi”的基地址的指针。可以完全合法地重新分配s,以指向非const char。如果您想要一个指向只读字符串的常量指针,则需要const char const* s;
Rob11311 2014年

38

对于那个确切的例子,malloc几乎没有用。

需要malloc的主要原因是,当您拥有的生命周期必须不同于代码范围时。您的代码在一个例程中调用malloc,将指针存储在某个地方,最后在另一个例程中调用free。

第二个原因是C无法知道堆栈上是否有足够的空间用于分配。如果您的代码需要100%健壮,则使用malloc更为安全,因为这样您的代码可以知道分配失败并进行处理。


4
内存生命周期以及何时以及如何取消分配内存的相关问题是许多常见库和软件组件的重要问题。他们通常有一个证据充分的规则:“如果你传递一个指向这个我的套路之一,你需要有malloc分配它,我会跟踪它,并释放它,当我用它做。 ” 讨厌的bug的常见来源是将指向静态分配的内存的指针传递给此类库。当库尝试使用free()时,程序崩溃。我最近花了很多时间来修复其他人写的错误。
鲍勃·墨菲,

您是说真正使用malloc()的唯一一次机会是,有一段代码在程序生命周期中将被多次调用,而该代码段将被多次调用并且需要“清除”,因为malloc ()伴随有free()?例如,在诸如“命运之轮”之类的游戏中,您猜测之后将输入放置在指定的char数组中,那么可以释放malloc()大小的数组用于下一个猜测吗?
史密斯·威尔

数据的生存期确实是使用malloc的真正原因。假设一个抽象数据类型由一个模块表示,它声明一个List类型,以及从列表中添加/删除项目的例程。这些项目值需要复制到动态分配的内存中。
Rob11311 2014年

@Bob:那些讨厌的错误,使分配器释放内存的约定更加优越,毕竟您可能正在回收它。假设您用calloc分配了内存以改善引用的局部性,这暴露了那些库的破坏性,因为您只需要为整个块调用一次free。幸运的是,我不必使用将内存指定为“ malloc-ed”的库,这不是POSIX的传统,很可能会被视为错误。如果他们“知道”您必须使用malloc,为什么库例程不为您这样做?
Rob11311 2014年

17

与像hello world示例这样的静态声明(在编译时进行处理,因此不能更改大小)相比,malloc是一种在运行时分配,重新分配和释放内存的绝佳工具。

因此,当您处理任意大小的数据(例如读取文件内容或处理套接字)并且不知道要处理的数据长度时,Malloc总是有用的。

当然,在一个简单的示例(如您给出的示例)中,malloc并不是神奇的“正确的工具,可以完成正确的工作”,但是对于更复杂的情况(例如,在运行时创建任意大小的数组),这是唯一的方法走。


7

如果您不知道需要使用的确切内存大小,则需要动态分配(malloc)。例如,当用户在您的应用程序中打开文件时。您将需要将文件的内容读取到内存中,但是您当然并不预先知道文件的大小,因为用户是在运行时现场选择文件的。因此,从根本上来说,malloc当您不知道要处理的数据量时就需要它。至少那是使用的主要原因之一malloc。在您的示例中,使用了一个简单的字符串,您已经在编译时知道它的大小(而且您不想修改它),因此动态分配它没有多大意义。


稍微偏离主题,但是...您必须非常小心,不要在使用时造成内存泄漏malloc。考虑以下代码:

int do_something() {
    uint8_t* someMemory = (uint8_t*)malloc(1024);

    // Do some stuff

    if ( /* some error occured */ ) return -1;

    // Do some other stuff

    free(someMemory);
    return result;
}

您看到这段代码有什么问题吗?malloc和之间有一个条件返回语句free。乍一看似乎还可以,但请考虑一下。如果出现错误,您将返回而不释放分配的内存。这是内存泄漏的常见原因。

当然,这是一个非常简单的示例,在这里很容易看到错误,但是请想象数百行代码中散布着指针,mallocs,frees和各种错误处理。事情很快就会变得非常混乱。这是在适用的情况下我更喜欢现代C ++而不是C的原因之一,但这是另一个主题。

因此,无论何时使用malloc,请始终确保尽可能多地保留您的内存free


很好的例子!走的路^ _ ^
Musa Al-hassy

6
char *some_memory = "Hello World";
sprintf(some_memory, "Goodbye...");

是非法的,字符串文字是const

这将在堆栈或全局上分配一个12字节的char数组(取决于声明的位置)。

char some_memory[] = "Hello World";

如果要为进一步操作留出空间,可以指定数组的大小应更大。(不过,请不要在堆栈上放入1MB。)

#define LINE_LEN 80

char some_memory[LINE_LEN] = "Hello World";
strcpy(some_memory, "Goodbye, sad world...");
printf("%s\n", some_memory);

5

有必要分配内存的原因之一是要在运行时进行修改。在这种情况下,可以使用堆栈上的malloc或缓冲区。向指针分配“ Hello World”的简单示例定义了“通常”在运行时无法修改的内存。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.