考虑以下:
int* x = calloc(3,sizeof(int));
x[3] = 100;
它位于一个函数内部。
编译和运行程序时没有错误,但是当我用valgrind运行它时,我得到一个“4号无效写入”。
我知道我在使用calloc分配的内容之外访问内存,但我正在尝试了解实际发生的情况。
堆栈中的某些地址(?)是否仍然具有值100?因为必须有比我用calloc分配的内存更多的可用内存。 valgrind错误更像是“嘿,你可能不是故意这样做”?
我知道我在使用calloc分配的内容之外访问内存,但我正在尝试了解实际发生的情况。
“实际发生的事情”没有明确定义;它完全取决于被覆盖的内容。只要您不覆盖任何重要的内容,您的代码就会按预期运行。
最终可能会破坏动态分配的其他数据。你可能最终破坏了一些堆簿记。
该语言不对数组访问强制执行任何类型的边界检查,因此如果您读取或写入数组的末尾,则无法保证将会发生什么。
堆栈中的某些地址(?)是否仍然具有值100?
首先,calloc
在堆上分配内存而不是堆栈。
现在,关于错误。
大多数情况下,程序运行时有足够的可用内存。但是,当您为x字节分配内存时,内存管理器会查找一些具有相同大小的内存空闲块(如果calloc
请求更大的内存来存储一些辅助信息,则可能会更多),对此之后的字节没有任何保证。 chunk用于,甚至不保证它们不是只读的,或者可以由程序访问。
所以一切都会发生。如果内存只是等待你的程序使用它,没有什么可怕的事情发生,但如果你的程序中的其他东西使用了那个内存,那么这些值会变得混乱,或者最糟糕的是程序可能会因访问不应访问的内容而崩溃。
所以应该非常认真地对待valgrind错误。
C语言不需要对数组访问进行边界检查,并且大多数C编译器都不实现它。此外,如果你使用一些变量大小而不是常量值3,那么在编译期间数组大小可能是未知的,并且无法检查访问是否不受限制。
这样做的行为就是所谓的undefined behavior。字面上任何事情都可能发生,或者根本没有。
即使你是愚蠢的,我给你额外的积分来测试Valgrind。
实际上,您可能会在数组后的内存空间中找到值100。
小心nasal demons。
对于在x[3]
之后的空间分配的内容或未来将在那里写的内容,我们无法保证。 alinsoar提到x[3]
本身不会导致未定义的行为,但您不应该尝试从那里获取或存储值。通常,您可能会毫无问题地编写和访问此内存位置,但编写依赖于分配到阵列之外的代码会让您在将来很难找到错误。
堆栈中的某些地址(?)是否仍然具有值100?
使用calloc或malloc时,数组的值实际上不在堆栈中。这些调用用于动态内存分配,这意味着它们被分配在称为“堆”的单独内存区域中。这允许您从堆栈的不同部分访问这些数组,只要您有指向它们的指针即可。如果数组在堆栈上,写入超出边界将有可能覆盖函数中包含的其他信息(例如在最坏的情况下返回位置)。
你正在为3个整数元素分配内存但访问第4个元素(x[3]
)。因此,来自valgrind的警告信息。编译器不会抱怨它。