getchar()和malloc在不应该返回时返回好的结果

时间:2014-11-17 20:47:09

标签: c malloc getchar

任何人都可以解释一下为什么这段代码能够完美运行吗?

int main(int argc, char const *argv[])
{
    char* str = (char*)malloc(sizeof(char));

    int c, i = 0;
    while ((c = getchar()) != EOF)
    {
        str[i] = c;
        i++;
    }

    printf("\n%s\n", str);

    return 0;
}

当我输入例如" aaaaaassssssssssssdddddddddddddddddddddddd时,这个程序不应该崩溃吗?这是我得到的输入:

aaaaaassssssssssssddddddddddddddd
aaaaaassssssssssssddddddddddddddd

我真的不明白为什么会这样。

1 个答案:

答案 0 :(得分:1)

正如你可能已经发现你超出了sizeof(char)(~1个字节)的内存块,你已经让malloc给你了,而你正在打印一个你没有特别为null终止的字符串。 / p>

这两件事中的任何一件都可能导致崩溃等现象,但现在却不行。超出你分配的内存块只意味着你遇到了你没有请求malloc给你的内存。无论如何它可能是内存malloc给你的,最小分配大于64字节也不会特别令人惊讶。此外,由于这是您在堆中分配内存的唯一位置,因此您不太可能覆盖在其他位置使用的内存地址(即,如果您分配了第二个字符串,则可能会超出第一个字符串的缓冲区并写入用于第二个字符串)。即使您有多个分配,您的程序也可能不会崩溃,直到您尝试写入操作系统尚未分配给该进程的内存地址。通常,操作系统将虚拟内存分配为页面,然后在进程内使用内存分配器(如malloc)来分发该内存并从操作系统请求更多内存。您可能已经为进程分配了几MB的读/写虚拟地址空间,并且在超过该空间之前不会崩溃。如果你试图写入包含你的代码的内存,你可能会因为操作系统保护写入而导致崩溃(或者如果没有,你会因为垃圾指令被执行而崩溃)。这可能足以说明为什么你没有因为溢出而崩溃。我建议通过向其发送更多数据来进行实验,以查看在没有崩溃的情况下可以正常工作的程度,尽管它可能因运行而异。

现在你可能崩溃或得到错误行为的另一个地方是打印你的字符串,因为printf假定一个空字节终止字符串,它从指针的地址开始并打印,直到它读取一个值为0的字节。由于你没有自己初始化内存,这可能是永远的。但是,它在正确的位置终止了打印。这意味着'刚刚发生'的字节为0.但这是一种简化。在“合理的”现代操作系统上,内核将零(写入0)分配给进程的内存,以防止泄漏来自内存的先前用户的信息。由于这是你完成的第一个/唯一的分配,内存是闪亮和干净的,但是你释放了内存,之前malloc可能会重用它,然后它会从你的进程编写的东西中获得非零值。

现在有用的建议可以在将来检测这些问题,即使是看起来效果很好的程序。如果您正在使用Linux(在OS X上,您需要安装它)我建议通过valgrind运行“小”程序,看看它们是否会产生错误。作为练习和简单的方法来了解输出看起来像你已经知道的错误,尝试在这个程序。由于valgrind减慢了速度,你可能会因为运行一个“大”程序而感到沮丧,但是“小”会覆盖大多数单个项目(即总是为学校项目运行valgrind并修复错误)。

有关您的程序运行环境的其他信息可能会导致对实现特定行为的进一步说明。即C实现或OS内存归零行为。