请告诉我堆栈和堆之间的区别与下面的代码
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信号。
由于
答案 0 :(得分:9)
无法保证这些调用中的任何一个实际写入无法访问的内存(这会触发段错误)。程序有权写入该内存并且不会触发段错误,但会覆盖与您的阵列无关的其他一些内部数据。这当然可能会导致程序中其他地方的意外影响。
通常,这称为未定义的行为。当你写出数组的边界时,没有任何承诺会发生什么,任何可能发生或可能不发生的事情。
答案 1 :(得分:3)
引用数组边界外的项是未定义的行为,这意味着任何事情都可能发生(异常,没有异常或其他)。 arr[5]
赋值不会导致错误的原因可能是因为该值仍在有效堆栈空间内(但在较长时间运行的应用程序中可能会导致错误)。对分配的数组的无效分配可能导致写入属于该进程的存储器中的页面,因此不会导致错误。但这可能会在不同的运行中发生变化。即使地址属于进程地址空间之外的页面,它依赖于操作系统实际发生的情况。
答案 2 :(得分:1)
堆是malloc()内存块的内存。
a [4000] = 8;没有失败,因为这是一个运气,它没有落入其他进程的内存地址。这只是偶然的
答案 3 :(得分:1)
您突出显示的所有案例都代表“未定义的行为”。
在某些情况下,这是一个noop,在其他情况下,这是一个分段错误。
“未定义行为”的特别之处在于,它可能会在预期的一段时间内发挥作用,但随后突然开始产生“不良副作用”(即分段错误)。这使得在生产中调试和重现这些条件变得非常困难。
答案 4 :(得分:1)
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时。
分配器可以从更大的物理分配中存储多块内存。这个底层实现因平台而异。
答案 5 :(得分:1)
缓冲区溢出是未定义的行为。如果缓冲区溢出在堆栈上,则会在星期一崩溃,如果它在堆上,则会在Tuedsay上崩溃。它们只是未定义的行为。
这是C段,表示它是未定义的行为:
(C99,6.5.6p8)“如果结果指向一个超过数组对象的最后一个元素的那个,那么 不得用作被评估的一元*运算符的操作数。“
当然,[]
是伪装的一元*
运算符:
(6.5.2.1p2)“下标运算符[]的定义是E1 [E2]与(*((E1)+(E2)))相同。”