【问题标题】:Difference between Stack And Heap栈和堆的区别
【发布时间】:2012-02-17 02:37:36
【问题描述】:

请根据下面的代码告诉我堆栈和堆之间的区别

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 错误。但我不明白为什么 a[4000] 不会给我任何运行时错误,即 SIGSEGV 信号。

谢谢

【问题讨论】:

    标签: c linux malloc undefined-behavior allocator


    【解决方案1】:

    不能保证这些调用中的任何一个实际上都会写入不可访问的内存(这会触发段错误)。程序很可能有权写入该内存并且不会触发段错误,但会覆盖与您的数组无关的其他一些内部数据。这当然可能会在程序的其他地方产生意想不到的影响。

    这通常称为未定义的行为。当你写出数组的边界时,没有任何承诺会发生什么,任何事情都可能发生,也可能不会发生。

    【讨论】:

    • 而且,更糟糕的是,意想不到的影响可能意味着未检测到的数据损坏。
    • @sth,感谢您澄清我的疑问。感谢所有回复的人。
    【解决方案2】:

    引用数组边界之外的项目是未定义的行为,这意味着任何事情都可能发生(异常、无异常或其他)。 arr[5] 赋值没有导致错误的原因可能是因为该值仍在有效的堆栈空间内(但它可能会导致稍后在运行时间较长的应用程序中出错)。对已分配数组的无效分配可能导致写入属于该进程的内存中的页面,因此不会导致错误。但这可能会因跑步而改变。即使地址属于进程地址空间之外的页面,它也取决于操作系统实际发生的情况。

    【讨论】:

    • 其中更长的时间可能被定义为只是从该函数返回
    • @mark 所以当分配的数组无效赋值也会导致分段错误?
    • @NikhilTej:是的,这当然是可能的。
    • @NikhilTej:从技术上讲,当你幸运的时候。不走运意味着在生产环境中损坏数据之前无法检测到错误。
    【解决方案3】:

    堆是您从中 malloc() 内存块的内存。

    a[4000] = 8;没有失败,因为它没有落入其他进程的内存地址是幸运的。只是偶然

    【讨论】:

      【解决方案4】:

      您突出显示的所有情况都代表“未定义的行为”。

      在某些情况下它是一个 noop,而在其他情况下它是一个分段错误。

      “未定义的行为”特别糟糕的是,它可能会在一段时间内按预期工作,但随后突然开始产生“不良副作用”(即分段错误)。这使得在生产环境中调试和重现这些条件变得非常困难。

      【讨论】:

        【解决方案5】:
        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
        }
        

        但我不明白为什么 a[4000] 不会给我任何运行时错误,即。 segsev 信号。

        它将在另一个平台或架构上。这真的没关系 - 你必须始终避免使用 UB。

        无论如何,区别在于系统分配器的实现(假设编译器没有将 malloc 的结果放在堆栈上)。

        你的分配器如何管理和分配内存是一个你不应该依赖的实现细节,尤其是当你到处乱扔 UB 时。

        分配器可以从更大的物理分配中分配内存。此底层实现因平台而异。

        【讨论】:

          【解决方案6】:

          缓冲区溢出是未定义的行为。如果缓冲区溢出在堆栈上,则周一可能会崩溃,如果在堆上,则可能会在周二崩溃。它们只是未定义的行为。

          这是 C 段落,它说它是未定义的行为:

          (C99, 6.5.6p8) "如果结果指向数组对象的最后一个元素,它 不得用作被评估的一元 * 运算符的操作数。”

          当然[] 是伪装的一元* 运算符:

          (6.5.2.1p2) "下标运算符[]的定义是E1[E2]等同于(*((E1)+(E2)))。"

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-05-10
            • 1970-01-01
            • 2016-01-19
            • 2016-08-18
            • 2020-10-24
            • 2015-01-15
            • 2011-11-20
            • 2020-08-24
            相关资源
            最近更新 更多