在C语言中为矩阵进行内存分配

7
为什么以下代码会导致分段错误?(我试图创建两个相同大小的矩阵,一个使用静态分配,另一个使用动态分配)
#include <stdio.h>
#include <stdlib.h>

//Segmentation fault!
int main(){
    #define X 5000
    #define Y 6000

    int i;
    int a[X][Y];

    int** b = (int**) malloc(sizeof(int*) * X);
    for(i=0; i<X; i++){
        b[i] = malloc (sizeof(int) * Y);
    }
}

奇怪的是,如果我注释掉其中一个矩阵定义,代码就可以正常运行。就像这样:

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

//No Segmentation fault!
int main(){
    #define X 5000
    #define Y 6000

    int i;
    //int a[X][Y];

    int** b = (int**) malloc(sizeof(int*) * X);
    for(i=0; i<X; i++){
        b[i] = malloc (sizeof(int) * Y);
    }
}

或者

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

//No Segmentation fault!
int main(){
    #define X 5000
    #define Y 6000

    int i;
    int a[X][Y];

    //int** b = (int**) malloc(sizeof(int*) * X);
    //for(i=0; i<X; i++){
    //  b[i] = malloc (sizeof(int) * Y);
    //}
}

我正在32位的Linux机器上运行gcc。 编辑:检查malloc()是否成功:
#include <stdio.h>
#include <stdlib.h>

//No Segmentation fault!
int main(){
    #define X 5000
    #define Y 6000

    int i;
    int a[X][Y];

    int* tmp;
    int** b = (int**) malloc(sizeof(int*) * X);
    if(!b){
        printf("Error on first malloc.\n");
    }
    else{
        for(i=0; i<X; i++){          
            tmp = malloc (sizeof(int) * Y);
            if(tmp)
               b[i] = tmp;
            else{
               printf("Error on second malloc, i=%d.\n", i);
               return;
            }
        }
    }    
}

当我运行它时,没有任何输出(除了"Segmentation fault")。

尝试使用fprintf输出到stderrprintf会输出到缓冲的stdout,因此如果程序崩溃,你可能会丢失输出。 - David Thornley
你能否打印出i并查看在失败之前它进入了循环多少次? - Michael Dorgan
8个回答

6

您的a变量在32位系统上需要120 MB的堆栈空间,计算方法为5000 * 6000 * 4。这可能会违反某些限制,导致分段错误。

此外,当然也有可能malloc()在某个时刻失败,这可能会导致您引用了一个NULL指针。


我考虑过那个问题,但是如果那样的话,我仍然无法想明白第三段代码如何运作。无论如何,我尝试检查了 malloc() 的结果,显然它们都成功了。 - Snogzvwtr

2

尝试在GCC中增加堆和栈限制:

gcc -Wl,--stack=xxxxx -Wl,--heap=yyyyy

尝试了,但是出现了“未识别选项'--stack'”的错误。 从我检查的情况来看,这些选项似乎只适用于Windows。而我使用的是Linux系统。 - Snogzvwtr
实际上,在Linux中,这些选项仅适用于i386 PE目标。 - Juliano

2
您遇到了分段错误,这意味着您的程序试图访问未被分配给其进程的内存地址。数组a是局部变量,因此从堆栈中分配内存。正如unwind所指出的那样,a需要120 MB的存储空间。这几乎肯定大于操作系统为您的进程分配的堆栈空间。一旦for循环走出堆栈末尾,您就会遇到分段错误。
在Linux中,堆栈大小由操作系统而不是编译器控制,因此请尝试以下操作:
$ ulimit -a

在响应中,您应该看到类似于以下内容的行:-
stack size (kbytes)            (-s)  10240

这意味着每个进程只有10兆字节的存储空间,这对于你的大型数组来说远远不够。你可以使用“ulimit -s ”命令来调整堆栈大小,但我怀疑它不会允许你选择120兆字节的堆栈大小!最简单的解决方案是将“a”变成全局变量而不是局部变量。

将'a'变成全局变量确实解决了问题。谢谢!(我正在处理的真正程序比这个玩具示例复杂得多,但同样的原则适用,所以我想现在可以回去继续工作了)。 - Snogzvwtr

1

这些是相当大的分配。你尝试过检查malloc()是否成功吗?

你可以使用malloc()为所有数组分配内存,并每次检查它是否成功。


1

栈溢出(多么恰当!)可能导致分段错误,这似乎是你在这里看到的。

在第三种情况下,堆栈指针被移动到一个无效地址,但由于程序随后退出而没有被用于任何事情。如果在堆栈分配之后放置任何操作,您应该会得到段错误。


1
也许编译器只是将堆栈指针更改为某个大值,但从未使用它,因此从未导致内存访问冲突。
尝试在第三个示例中初始化A的所有元素?您的第一个示例尝试在堆栈上分配B在A之后,并且访问如此高的堆栈(在第一次分配给B时)可能是导致段错误的原因。

谢谢,我现在明白问题了。我之前没有意识到,在为a分配了那么多空间之后访问堆栈(以创建b)可能会导致段错误。 - Snogzvwtr

0

你的第三段代码也不起作用(至少在我的系统上是这样)。

尝试在堆上为数组a分配内存(当维度很大时)。


0

两个矩阵都超出了您的内存限制。您只能一次分配一个。

如果您将Y定义为3000而不是6000,则您的程序不应该发出segfault。


我确实有超过120MB的可用内存。一定有正确的方法来做这件事。(实际上,我在我的实际程序中需要的矩阵甚至更大——这只是一个玩具示例,以帮助找出问题所在)。 - Snogzvwtr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接