0
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char *p;
p = (char *)malloc(4*sizeof(char));
strcpy(p, "abcdabcd");
printf("%s\n", p);
free(p);
printf("%s\n", p);
return 0;
}

我尝试在 Ubuntu 上运行上述代码。这里我从 malloc 分配 4 个字节的内存。然后我尝试将 8 个字节复制到 malloc 分配的内存中。我没有收到任何警告或错误。我尝试释放内存块并尝试使用被释放的相同内存,但再次没有任何问题。它打印了正确的字符串。有人可以解释这种行为吗?

4

7 回答 7

3

这种行为无法得到有意义的解释。您的程序表现出未定义的行为,这意味着任何事情都可能发生。“任何东西”还包括您在实验中观察到的行为,即“打印正确的字符串”。

今天它“打印了正确的字符串”。明天它可能会崩溃。后天它可能会格式化您的硬盘。在未定义行为的概念下,这是完全允许的。

于 2012-04-17T19:13:12.637 回答
2

Aparrently正确的操作属于[未定义的行为]类。

于 2012-04-17T19:13:22.830 回答
2

当程序启动时,它可以通过操作系统访问虚拟内存。通常虚拟页面有一些最小大小(比如 4 kb),这是处理它们的最小大小。

因此,您使用低于此页面大小的 malloc 请求内存,并且整个块都可用。Some/None/All 此块可能被其他程序使用。但它在技术上仍然存在:

1 byte | 1 byte | 1 byte | 1 byte | more memory you don't have....
 ^ char p points here

然后,您开始从 p开始将字符串复制到内存中。所以你得到:

a | b | c | d | the rest of the chars start dumping into somebody elses space!

这种重叠是未定义的。换句话说,我们可能有未使用的内存,所以今天一切正常。明天,该物理内存空间由操作系统持有,我们无法覆盖它。第二天,我们打破了一些其他的程序。在那之后的第二天,我们就出现了故障。

正如其他人所指出的,C 不会照顾你。

于 2012-04-17T19:24:49.820 回答
1

C 语言不保证任何内存检查。基本上,如果您写入不属于您的内存(例如,您写入的内存超出了数组的范围),或者您从不属于您的内存中读取(例如,您通过释放它从您放弃的内存中读取) ) 不知道会发生什么。

但是,操作系统可能会也可能不会阻止您执行这些操作。通常,如果发生这种情况,您会收到 "Bus Error" 或"Segmentation Fault"。但是,也不能保证这两种情况都会发生。

最好的办法是防御性地编写代码。跟踪您的数组有多长,然后断言或以其他方式检查当您复制到数组时您不会超过数组大小。

您可以做的另一件事是调试。Valgrind是一种非常有效的工具,可以解决您所描述的问题。它可以指示您读取或写入尚未 malloc 的内存的两种情况。它还可以识别泄漏,例如当您 malloc 内存然后忘记释放它时。

于 2012-04-17T19:17:49.720 回答
0

我相信它会沿着块对齐分配,这就是为什么它在这种情况下给你一些神奇的额外内存。尝试将其提高到更大的字节倍数(例如 16,64 等,它应该更准确,如果超出它可能会出现段错误)。尽管请记住,如果 malloc'd 块之后的内存也是程序内存空间的一部分……那么您也不会遇到段错误。

一般来说,只要你留在你的程序内存段中,你就可以愉快地读/写你想要的一切(当然风险自负:D)。

如果您真的想检查您的程序,请使用/in 'valgrind' 运行它

于 2012-04-17T19:19:42.660 回答
0

就像所有其他人所说的那样,您的代码会导致未定义的行为(任何事情都可能发生)。

您的堆内存分配器 (malloc) 不负责释放后存储在该区域中的数据

了解 malloc 的职责将帮助您解释为什么看到结果、为什么它不正确的编程实践以及为什么它不能一直工作。

Malloc 是 glibc 中的堆内存分配器(_int_malloc 和 _int_free)。你可以在这里查看它的代码 [ http://code.woboq.org/userspace/glibc/malloc/malloc.c.html#_int_malloc]

有人可以解释一下这种行为吗?

一个滑稽的例子将有助于快速澄清你的问题。

假设您正在从一家汽车旅馆 ( sbrk(2) ) 租一个房间 ( memory ),而该汽车旅馆没有任何房间的钥匙 ( memory )!. 你去找接待员 ( malloc ) 并要求一个房间 ( memory ),她给你一个可用的房间,你拿下房间 ( memory ) 并使用它。 你已经完成了它,现在向她保证你不会再次使用它(释放)。她在她的书中做了一个条目,你的房间(记忆)是免费的,可以分配给其他人。把房间给别人或不给别人,完全是她的心愿。


过了一段时间你又来看看你的房间(接待员没有检查你),如果你发现没有人使用房间,你很幸运(它看起来和你离开时一样凌乱!)

接待员(malloc)的工作是尽快为您分配一个房间(内存),相信您在离开后会遵守诺言(免费)。她的工作不是阻止您使用任何房间(记忆) !

如果您访问不允许您访问的地方(只读区域),所有者(内核)会变得愤怒(异常)。

我是新手,我不确定这样回答是否合适。如果我错了,请告诉我我强烈建议你通过 malloc

于 2014-09-10T09:58:48.477 回答
0

每个说您调用未定义行为的人都是正确的。

它似乎正常工作的原因可能是您平台上堆管理器的特定实现的副作用。例如,许多内存管理器将分配四舍五入到一些方便的大小,最小的大小通常是 8 或 16 字节。你永远不应该依赖这样的实现细节。

一些平台有工具可以在您开发程序时尝试捕捉这些类型的假设。

于 2012-04-17T19:33:16.970 回答