2010-04-15 94 views
8

为什么下面的代码导致分段错误? (我试图创建一个同样大小的两个矩阵,一个静态和其他与动态分配)C中矩阵的内存分配

#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位的机器上运行GCC在Linux上。

编辑:检查是否的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; 
      } 
     } 
    }  
} 

什么也没有打印出来,当我运行它(当然期待为 “段错误”)

+0

尝试'fprintf'到'stderr'。 'printf'打印到'stdout',它被缓冲,所以如果程序崩溃了,你可能会失去输出。 – 2010-04-15 15:50:14

+0

你还可以打印出我看看在失败之前进入循环多远? – 2010-04-15 15:56:45

回答

2

您正在收到分段错误,这意味着您的程序试图访问尚未分配给其进程的内存地址。数组a是一个局部变量,因此从堆栈分配内存。由于unwind指出a需要120兆字节的存储空间。这几乎肯定大于操作系统分配给您的进程的堆栈空间。只要for循环离开堆栈的末尾,就会出现分段错误。

在Linux中的堆栈大小由操作系统控制,没有编译器,请尝试以下操作: -

$ ulimit -a 

在响应中,你应该看到一行是这样的: -

stack size (kbytes)   (-s) 10240 

这意味着每个进程都可以获得10Mbyte的存储空间,远远不够您的大型阵列。

您可以使用ulimit -s <stack size>命令调整堆栈大小,但我怀疑它不会允许您选择120Mbyte堆栈大小!

最简单的方法是使a为全局变量而不是局部变量。

+0

使'a'成为一个全局变量的确有诀窍。谢谢! (我正在研究的真正的程序实际上比这个玩具的例子复杂得多,但同样的原则适用,所以现在我想我可以回到它了)。 – Snogzvwtr 2010-04-15 18:51:05

1

这些都是相当大的分配。你有没有试过检查以确保malloc()成功?

您可能会对所有阵列使用malloc(),并检查每次是否成功。

6

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

此外,当然有可能malloc()在某些时候失败,这可能导致您取消引用NULL指针。

+0

我虽然那样,但我仍然无法用第三代码工作,如果是这样的话。 无论如何,我试图检查malloc()的结果,显然他们都成功了。 – Snogzvwtr 2010-04-15 15:29:13

0

你的第三个代码也不能用(至少在我的系统上)。

试着将内存分配给堆上的数组a而不是(当尺寸很大时)。

2

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

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

尝试过,但得到了“无法识别的选项” - 堆栈“”。 从我检查,显然,这些选项只适用于Windows。我在Linux上。 – Snogzvwtr 2010-04-15 15:30:13

+0

实际上,在Linux中,这些选项仅适用于i386 PE目标。 – Juliano 2010-04-15 16:52:22

0

两个矩阵不适合在你的记忆的极限。您一次只能分配一个。

如果您将Y定义为3000而不是6000,那么您的程序不应该发出段错误。

+0

我确实有(多)超过120MB的可用内存。一定有办法做到这一点。 (实际上,我在我的实际程序中需要的矩阵甚至更大 - 这只是一个玩具示例,可以帮助找出问题所在)。 – Snogzvwtr 2010-04-15 15:36:32

1

堆栈溢出(如何恰当!)可能导致出现分段错误,这是您看到的。

在第三种情况下,堆栈指针正被移动到一个无效地址,但是因为程序退出,所以没有被用于任何事情。如果你在堆栈分配后进行了任何操作,你应该得到一个段错误。

1

也许编译器只是将堆栈指针更改为一些大的值,但从不使用它,因此从不导致内存访问冲突。

尝试在第三个例子中初始化A的所有元素?你的第一个例子试图在堆栈中的A之后分配B,并且访问高的堆栈(在第一次分配给B时)可能是导致段错误的原因。

+0

谢谢,我想现在我明白了这个问题。我之前并没有意识到在为这个堆栈分配足够的空间之后访问堆栈(创建b)*会导致段错误。 – Snogzvwtr 2010-04-15 18:46:46