请告诉我堆栈和堆之间关于以下代码的区别
int main()
{
int arr[3];
int *a;
arr [5] = 6; // out of bound but it will not give error.
arr [3000] = 8 ; //SIGSEGV
a = malloc (sizeof (int));
a[4] = 6;
a[4000] = 8; //No error
}
我知道arr是一个静态数组,当我执行arr[3000]时,我正在访问其他进程的地址,这会导致SIGSEGV错误。但我不明白为什么[4000]不会给我任何运行时错误,即SIGSEGV信号。
感谢
不能保证这些调用中的任何一个都会写入不可访问的内存(这会触发segfault)。同样可能的是,程序有权写入该内存,并且不会触发segfault,而是覆盖与数组无关的其他一些内部数据。当然,这可能会在程序的其他地方产生意想不到的影响。
通常,这被称为未定义行为。没有做出任何承诺,当您在数组的边界之外写入时会发生什么,任何事情都可能发生,也可能不会发生。
arr[5]
赋值没有导致错误的原因可能是该值仍在有效的堆栈空间内(但它可能会在稍后运行时间较长的应用程序中导致错误)。对已分配数组的无效分配可能导致写入属于进程的内存中的页面,因此不会导致错误。但这种情况可能会随着跑步而改变。即使地址属于进程地址空间之外的页面,也取决于操作系统实际会发生什么。
堆是从中malloc()内存块的内存。
a[4000]=8;没有失败,因为幸运的是它没有落入其他进程的内存地址中。这只是偶然的
您高亮显示的所有案例都表示"未定义的行为"。
在某些情况下,这是一个失误,而在另一些情况下,它是一个分割错误。
"未定义行为"特别糟糕的是,它可能会在一段时间内按预期工作,但随后突然开始产生"不良副作用"(即分割错误)。这使得在生产中调试和再现这些条件变得非常困难。
int main()
{
int arr[3];
int *a;
arr [5] = 6; // out of bound but it will not give error.
// J: False - it is undefined. expect raptors, or something.
arr [3000] = 8 ; //SEGSEV
// J: Now you see the effects of undefined behavior, even though you did not in a previous invalid access.
a = malloc (sizeof (int));
a[4] = 6; // J: Still undefined behavior
a[4000] = 8; //No error
// J: Still undefined behavior
}
但我不明白为什么[4000]不会给我任何运行时错误,即,。segsev信号。
它将在另一个平台或架构上。这真的不重要-你必须始终避免UB。
无论如何,区别在于系统分配器的实现(假设编译器没有将malloc的结果放在堆栈上)。
分配器如何管理和分配内存是一个不应该依赖的实现细节,尤其是当您到处使用UB时。
分配器可以从更大的物理分配中分配内存。这种底层实现因平台而异。
缓冲区溢出是未定义的行为。缓冲区溢出如果在堆栈上,可能在周一崩溃,如果在堆上,则可能在周二崩溃。它们只是未定义的行为。
以下是C段,它说这是未定义的行为:
(C99,6.5.6p8)"如果结果指向数组对象的最后一个元素,则不得用作评估的一元*运算符的操作数。"
当然,[]
是一个伪装的一元*
算子:
(6.5.2.1p2)"下标运算符[]的定义是E1[E2]与(*((E1)+(E2))相同。"