`malloc()`刚刚分配的内存内容是什么?

5
我想了解当使用malloc()分配内存空间后指针具体保存了什么。手册告诉我calloc()会用零初始化已分配的内存空间。

malloc()函数分配大小为size字节的内存,并返回指向已分配内存的指针。内存未被初始化。如果size为0,则malloc()返回NULL或可稍后成功传递给free()的唯一指针值。

以及

calloc()函数为大小为nmemb个元素,每个元素大小为size字节的数组分配内存,并返回指向已分配内存的指针。内存设置为零。如果nmemb或size为0,则calloc()返回NULL或可稍后成功传递给free()的唯一指针值。

我为自己编写了一个非常简短的C程序示例:

int main() {
    char *dynamic_chars;
    unsigned amount;
    printf("how much bytes you want to allocate?\n");
    scanf("%d", &amount);

    dynamic_chars = (char*)malloc(amount*sizeof(char));
    printf("allocated:\n%s\n", dynamic_chars);

    free(dynamic_chars);
    return 0;

然而,当执行此代码时,它什么都没有输出。如果我自己初始化内存,例如使用循环将每个字节都初始化为 0xFFFF,那么程序会显示我期望的结果。内存空间实际上存在,因为我不会收到错误,声称我正在尝试访问未初始化的变量等。

由于内存空间通常不会被删除,而是标记为可重写的,我想知道通过执行我的程序,我是否应该能够看到以前使用过的随机字节的内存?但我什么也看不到,所以我对malloc()的工作方式非常困惑。

编辑1

关于malloc()或者可能是内存使用的一般性问题,对于我的程序来说很有趣: 如果我使用calloc()来分配内存,我可以通过监视它来跟踪我的程序的实际内存使用情况。例如,如果我告诉我的程序,每个calloc()要分配1,000,000,000字节的内存,我将在我的系统监视器中看到以下内容: Memory consumption when using <code>calloc()</code>

你可能可以想象,使用malloc()时,我什么也看不到。我理解,仅通过分配内存,我并没有真正使用它,但我仍然对为什么我的操作系统(Unix衍生版)不会将其识别为已使用而感到困惑。由于malloc()calloc()一样,返回物理地址到内存位置,我不明白为什么这个内存区域似乎实际上并未被操作系统保留。否则,我应该能在系统监视器中看到它,对吧? 如果我应该将此作为新问题发布,请告诉我。但我认为,由于问题仍然是关于malloc()的工作原理,因此它适合在这里。


在这种测试方式下,往往会无意中变成0。当你在真实程序中进行操作时,这种情况不会重复出现,并且之前释放的堆块会被回收利用。你不能做出任何假设。 - Hans Passant
2
如果内存以零开头,printf 将其视为 ""(零是终止字符)。如果您想查看实际值,则应使用另一种方法。 - Gwen
scanf("%d", &amount);scanf("%u", &amount); - Spikatrix
我认为你问题中的“编辑1”部分的答案可能是与实现/操作系统有关的,但由于calloc需要连续的内存而malloc可以使用碎片,因此malloc可以使用已经为程序分配的内存,而calloc需要保留“新”的内存。或者malloc可以推迟实际的预留,直到内存实际被使用的时候。 - diidu
注意:为什么要使用类型unsigned amount;?请查看malloc()接受的参数类型。 - chux - Reinstate Monica
3个回答

13
不,malloc() 返回未初始化的内存空间,其内容是不确定的。因此,尝试使用这个值会引发未定义行为
引用 C11 的附录 §J.2 中关于 未定义行为 的说明:

使用由malloc函数分配的对象的值。

在这种情况下,%s 期望一个以 null 结尾的 char 数组。然而,dynamic_chars 的内容是不确定的,可能根本不存在 null 终止符,这将导致访问超出边界的内存,从而引发 UB。
引用 C11 第 §7.22.3.5 章,关于 malloc 函数(我强调:)的说明:

malloc 函数分配一个指定大小的对象的空间,其值是不确定的

话虽如此,请参阅这篇关于为什么不需要在C中对malloc()和相关函数的返回值进行类型转换的讨论。

不要将其与初始化分配的内存的realloc混淆! - gsamaras
@gsamaras 我认为这个问题是关于 malloc() 的,不是吗? :) - Sourav Ghosh
Sourav确实是这样,但就在几天前,一个新手对这两个东西感到困惑。也许我应该删除我的评论? - gsamaras
3
在将不确定值作为char类型读取时可能会产生陷阱表示。即使在没有陷阱表示的系统上,将不确定的值传递给库函数(如printf)也是未定义的。点击此处进行更多讨论。 - M.M
1
@RadLexus 只是为了增加一些背景,C11特别提到了这种情况作为UB。请注意我的回答中的编辑。 - Sourav Ghosh
显示剩余3条评论

2
C语言未定义在获取内存块时其内容是什么。实际上,它很可能只包含之前物理内存中的内容。
如果该内存以前被您的程序使用并已释放,则很可能只会得到先前的内容。如果这是从操作系统重新请求的内存,则会得到操作系统放入其中的内容。大多数操作系统返回已经特别设置为“零”字节的内存,因为如果内存仍然包含先前某些其他程序的内容,那将是安全问题。
所有这些都不被任何标准保证,它只是实践中大多数系统的情况。

2

malloc为您分配内存并将指针设置为它。它不以任何方式初始化内存,因此分配的内存区域可以包含任何内容。由于它不包含字符串,因此无法通过打印字符串来读取其内容。相反,您可以按字节打印它,例如:

for(int i=0;i<amount*sizeof(char);i++)
{
    printf("%02x", (unsigned)dynamic_chars[i]);
}

1
这将不确定的值传递给库函数。C标准对此并不清楚,但DR451 Proposed Committee Resolution建议将其视为未定义行为。 - M.M
@M.M 我不明白为什么这段代码会有问题。 dynamic_chars 中的值确实是不确定的,但它们仍然只是值,我不明白为什么打印这些值的十六进制表示应该成为一个问题。 - Jabberwocky
@M.M 的观点很好,关于 %x,我进行了一些小的改进以修复它。然而,我很难理解为什么 printf 的行为会是未定义的,内容明显是(如 Michael Walz 所提到的),但我不明白为什么不可能打印出内存的内容。 - diidu
未初始化的内存在标准C中没有任何内容。听起来你想象它包含某个值。 - M.M
2
@M.M 好的,从规范角度来看我同意你的观点。然而,在实践中,为了使未初始化的内存不包含任何值,它需要在初始化之前是不存在的内存。你见过这样的实现吗?我没有,甚至无法想象。 - diidu
无论如何,可以肯定地说读取未初始化的内存对于研究或调试目的来说是安全的,但对于生产来说可能是不安全的。 - diidu

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