关于C#:预期未收到seg错误

Not receiving a seg fault when expected

我正在学习如何在C中使用指针和结构。自然地,我试图刻意破坏我的代码以进一步理解该语言的工作原理。这是一些可以正常使用的测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#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("
"
);

    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("
%d %d
"
, s_pair.x, s_pair.y );
}

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

然后,我决定修改部分代码,如下所示:

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

此修改未产生我期望的seg错误。尽管我超出了使用scanf函数为变量大小分配值时显式设置的缓冲区,但仍打印了所有"对"。

据我了解的指针(如果我错了,请纠正我),当我为类型对s_pair的指针调用malloc函数时,堆中的内存管理器将保留大小为size * sizeof(pair)的连续内存块。我所做的是,当我将for循环修改为条件i

如果我正确地理解了这一点,我的指针是否超出了其保留的内存限制,并且由于没有其他相邻数据被其他数据占用而恰好是明确的吗?缓冲区溢出时,这是正常现象吗?

补充一点,当我使用i

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

1
2
3
4
5
6
7
8
int size, i, *ptr;

scanf("%d", &size );

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

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

这将产生与上述完全相同的结果。在i

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

1
2
3
4
int i, array[10];

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

对于i <25的条件,我将获得段故障,而不会失败。当我将其更改为i <15时,不会出现段错误。

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

希望所有这一切对决定阅读一个有点生气的人的杂物的人都是有意义的。


欢迎来到光荣的C世界!

内存分配函数(malloccallocrealloc等)为您提供堆上的内存。当您调用其中之一而您的程序没有足够的空间时,它将进行系统调用以获取更多空间。但是,它不会以精确的增量执行此操作(通常会以一定数量的整个页面增量执行此操作)。当索引超出数组的末尾(甚至在数组的开始之前)时,您仍在程序合法地址空间的范围之内。只有当您离开程序所拥有的细分时,您才会获得细分违规。

我强烈建议使用Valgrind检查您的程序,尤其是在您故意通过破坏事物来了解内存的情况下。除其他事项外,它将在分配的两侧存储金丝雀值,以帮助您确定何时超出界限,并警告您两次释放和内存泄漏。


If I'm understanding this correctly, did my pointer exceed its reserved memory limit and just so happen to be in the clear because nothing adjacent and to the right of it was occupied by other data?

差不多了除非您不是"明晰"的,因为相邻的对象可能已被其他数据占用,并且您的代码只是踩在该内存上并更改了值。您可能永远不会注意到问题,或者您可能会在以后发现问题。无论哪种方式,它都是不确定的行为。


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

这完全取决于应该覆盖数据的位置。

  • 可能什么也没有,因为C lib尚未在此处分配程序,
  • 您可能已经覆盖了重要的管理信息,这些信息稍后会被使用,然后导致崩溃,
  • 或其他。

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


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