【问题标题】:Not receiving a seg fault when expected预期时未收到段错误
【发布时间】:2013-09-05 02:59:27
【问题描述】:

我正在学习如何在 C 中使用指针和结构。当然,我试图故意破坏我的代码以进一步了解该语言的工作原理。下面是一些测试代码,可以按我的预期工作:

#include <stdio.h>
#include <stdlib.h>

struct pair {
    int x;
    int y;
};

typedef struct pair pair;

void p_struct( pair ); //prototype

int main( int argc, char** argv ) {
    pair *s_pair;
    int size, i;

    printf( "Enter the number of pair to make: " );
    scanf( "%d", &size );
    getchar();
    printf( "\n" );

    s_pair = (pair*)malloc( size * sizeof(pair) );

    for( i = 0; i < size; i++ ) {
        s_pair[i].x = i;
        s_pair[i].y = i;
        p_struct( s_pair[i] );
    }

    getchar();

    return (EXIT_SUCCESS);
}

void p_struct( pair s_pair ) {
    printf( "\n%d %d\n", s_pair.x, s_pair.y );
}

如前所述,据我所知,这段代码是有效的。

然后我决定像这样修改部分代码:

for( i = 0; i < size + 3; i++ ) {
    s_pair[i].x = i;
    s_pair[i].y = i;
    p_struct( s_pair[i] );
}

此修改没有产生我预期的 seg 错误错误。尽管我超出了使用 scanf 函数为变量 size 赋值时明确设置的缓冲区,但所有“对”都被打印出来了。

据我了解指针(如果我错了,请纠正我),当我调用malloc 函数用于我的类型对 s_pair 的指针。我所做的是,当我将 for 循环修改为条件 i 时,我超出了最后分配的内存地址。

如果我理解正确的话,我的指针是否超出了它的保留内存限制,并且恰好是清晰的,因为它的右侧和附近没有被其他数据占用?这是溢出缓冲区时的正常行为吗?

补充一点,当我使用 i 的 for 循环条件进行测试时,我确实收到了 seg 错误。问题是,它仍然打印输出。如,当 size = 10 根据我制作的 p_struct 函数在屏幕上打印时,它会在屏幕上打印“0 0”对“24 24”。程序只有在到达底部的 getchar() 之一后才会因段错误而崩溃。我的程序究竟如何将值分配给超出缓冲区的对,将它们打印在屏幕上,然后突然决定在到达 getchar() 时因段错误而崩溃? i 似乎没有问题(尽管它仍然是错误的)。

作为记录,我还使用常规指针数组测试了这种行为:

int size, i, *ptr;

scanf( "%d", &size );

ptr = (int*)malloc( size * sizeof(int) );

for( i = 0; i < size + 15; i++ )
    ptr[i] = i;

这会产生与上面完全相同的结果。在 i 处,段错误似乎没有任何问题。

最后,我也用数组进行了测试:

int i, array[10];

for( i = 0; i < 25; i++ )
    array[i] = i;

对于条件 i ,我得到一个段错误,没有失败。当我将其更改为 i 时,我没有收到任何段错误。

如果我没记错的话,指针数组和数组之间的唯一区别是分配给数组的内存位于堆栈而不是堆上(对此不确定)。考虑到这一点,并考虑到 i 当 array[10] 不会产生任何段错误的事实,为什么 i 是个问题?在 for 循环期间,数组不是位于堆栈顶部吗?当它不关心 60 个额外字节时,为什么它会关心 100 个额外字节?为什么该数组缓冲区的上限不是一直到为整个堆栈保留的任意内存块的末尾?

希望所有这些对于决定阅读一个稍微醉酒的人的漫谈的人来说都是有意义的。

【问题讨论】:

  • 未定义的行为:当您执行未定义的事情时,您的代码不需要崩溃或段错误。当你做一些非常错误的事情时,应用程序会在某些平台上崩溃。不过,系统不需要捕捉你在做未定义的事情;这是C语言中最常见的隐藏错误来源。

标签: c arrays pointers struct segmentation-fault


【解决方案1】:

正如其他人所说,未定义的行为并不意味着您的程序在所有情况下都会崩溃。

这完全取决于覆盖数据的地方应该是什么。

  • 可能什么都没有,因为 C lib 没有在那里分配程序,
  • 您可能覆盖了稍后使用的重要管理信息,然后才会导致崩溃,
  • 或其他。

为了帮助您了解真正发生的事情,打印地址(例如printf("%p\n", s_pair); 或类似的东西)可能会有所帮助,以及将程序编译为可读的汇编助记符(例如gcc -S filename.c -o-

【讨论】:

    【解决方案2】:

    当您调用 malloc 时,您可能会获得比您需要的更多的内存,因为内存是按公共块大小的倍数分配的。如果块大小是 64 字节,而您只要求 10 字节,那么操作系统会给您 64 字节,因此您仍然可以访问超出您请求范围的内存,这是您的程序正在观察的行为。

    【讨论】:

    • 虽然这可能是一个原因,但在这种情况下可能不是:malloc() 可能以 8/16 字节的倍数分配(double/SSE 对齐)。提示:free(s_pair) 程序结束。
    【解决方案3】:

    欢迎来到C的光辉世界!

    内存分配函数(malloccallocrealloc 等)为您提供堆上的内存。当您调用其中一个并且您的程序没有足够的空间时,它会生成一个system call 以获得更多空间。但是,它不会以精确的增量来执行此操作(它通常会以某些数量的整页增量来执行此操作)。当您在数组末尾(甚至在数组开头之前)进行索引时,您仍然在程序的合法地址空间范围内。只有当您离开您的程序拥有的段时,您才会获得Segmentation Violation

    我强烈建议您使用Valgrind 来检查您的程序,尤其是当您故意尝试通过破坏事物来了解内存时。除其他外,它会在分配的任一侧存储金丝雀值,以帮助您确定何时访问越界并警告您双重释放和内存泄漏。

    【讨论】:

      【解决方案4】:

      如果我理解正确的话,我的指针是否超出了它的保留内存限制,并且恰好是清晰的,因为它的右侧和附近没有被其他数据占用?

      差不多。除了你不是“很清楚”,因为相邻的东西可能被其他数据占用,你的代码只是踩在那个内存上并改变了值。您可能永远不会注意到问题,或者您可能会在很久以后才注意到问题。无论哪种方式,它都是未定义的行为。

      【讨论】:

        猜你喜欢
        • 2011-07-11
        • 2019-06-28
        • 2013-04-05
        • 2017-06-15
        • 1970-01-01
        • 1970-01-01
        • 2018-04-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多