C 为什么堆没有更早地损坏?

C 为什么堆没有更早地损坏?,c,memory-management,heap-memory,C,Memory Management,Heap Memory,我试图在较低的层次上理解C如何管理内存。我在一个网页上发现了一些代码,其目的是教你糟糕的内存管理有多糟糕——因此我复制并粘贴了它,并编译: int main(int argc, char **argv) { char *p, *q; p = malloc(1024); q = malloc(1024); if (argc >= 2) strcpy(p, argv[1]); fre

我试图在较低的层次上理解C如何管理内存。我在一个网页上发现了一些代码,其目的是教你糟糕的内存管理有多糟糕——因此我复制并粘贴了它,并编译:

int main(int argc, char **argv) {
        char *p, *q;
        p = malloc(1024);
        q = malloc(1024);
        if (argc >= 2)
                strcpy(p, argv[1]);
        free(q);
        free(p);
        return 0;
}
测试用例是用generic命令执行的

/development/heapbug$ ./heapbug `perl -e 'print "A"x$K'`
对于
$K<1023
我没有预料到问题,但是对于
$K=1024
我预料到一个内核转储,但没有发生。长话短说,我开始为
$K>1033
设置segfaults

两个问题: 1) 为什么会这样?
2) 是否有一个公式说明系统的“容差”?

当写入超过分配内存的界限时,调用。这意味着您无法准确预测程序的行为。它可能崩溃,可能输出奇怪的结果,或者看起来工作正常

此外,进行看似不相关的更改(如添加未使用的局部变量或调用
printf
进行调试)可能会改变未定义行为的显示方式,使用不同的编译器或使用具有不同优化设置的同一编译器进行编译也是如此

程序可能崩溃并不意味着它会崩溃

也就是说,可能发生的事情与系统上如何实现
malloc
有关。它可能比用于对齐和记账的请求多留出几个字节。如果没有积极的优化,这些额外的对齐字节可能不会用于任何其他用途,因此您在写入它们时会遇到问题,但如果您进一步写入字节,而不是包含您损坏的
malloc
free
使用的内部结构,则会出现问题


但同样,你不能依赖这种行为。C依赖于开发者遵守规则,如果你没有坏事情发生。

未定义的行为就是这样。它可能会崩溃。可能不会。它可能会完美地工作。它可能会喝下你冰箱里所有的牛奶。它可能会偷走你最喜欢的一双鞋,然后带着它在泥地里跺脚

仅仅因为某件事是未定义的行为并不意味着它会立即变得如此明显。您已溢出此处的缓冲区,但未观察到后果。这可能是因为您实际上没有使用分配的第二个缓冲区,因此,如果您开始向该缓冲区写入数据,则不会对任何代码产生影响


这就是为什么像exist这样的工具可以查找可能并不总是产生明显或不希望的结果的错误。

根据我的理解,如果您溢出到应用程序用户空间(代码/堆栈/等)中控制的内存中它不能保证会导致coredump,并且确实会覆盖一些内存,这是由无意的缓冲区溢出确定的风险


一旦您开始尝试覆盖这些界限之外的数据,操作系统就更有可能阻止它。

写入未分配的内存是未定义的行为。结果没有具体说明。它可能会也可能不会导致碰撞。A可能会损坏其他内存地址的内容,但这将如何影响程序尚不得而知。

UB不符合“公式”。因此有“未定义”一词。没有“C如何管理内存”。只有特定的C实现如何管理内存,这是不同的。我个人并不认为研究特定实现的特性有多大价值。@JohnBollinger:我的意思是-这会是内存分页的副作用吗?