使用malloc获取2D数组的分段错误

时间:2014-10-01 07:45:57

标签: c arrays

我使用malloc初始化了一个2D数组,用于大图的邻接矩阵,然后根据边缘列表初始化每个索引为0或1。但是我得到了一个分段错误。这是我的代码。

#include <stdio.h>
#include <stdlib.h>
int MAX = 50000;
void clustering(int **adj);

int main()
{
  int i, j, k;  
  FILE *ptr_file1;
  int **adj;

  adj = (int **)malloc(sizeof(int *)*MAX);
  for(i=0;i<MAX;++i)
  adj[i] = (int *)malloc(sizeof(int)*MAX);

  struct adjacency
  {
     int node1;
     int node2;
  };
  struct adjacency a;

  ptr_file1 = fopen("Email-Enron.txt","r"); //Opening file containing edgelist of approx  37000 nodes.

  if (!ptr_file1)
    return 1;

  while(fscanf(ptr_file1,"%d %d",&a.node1, &a.node2)!=EOF)
  {
     adj[a.node1][a.node2] = 1;                   //Getting segmentation fault here   
     adj[a.node2][a.node1] = 1; 

  printf("adj[%d][%d] = %d   adj[%d][%d] = %d\n",a.node1,a.node2,adj[a.node1][a.node2],a.node2,a.node1,adj[a.node2][a.node1]);  
  }
  clustering(adj);
  return (0);
 }

这是我的输出

......
......
adj[85][119] = 1   adj[119][85] = 1
adj[85][154] = 1   adj[154][85] = 1
adj[85][200] = 1   adj[200][85] = 1
adj[85][528] = 1   adj[528][85] = 1
adj[85][604] = 1   adj[604][85] = 1
adj[85][661] = 1   adj[661][85] = 1
adj[85][662] = 1   adj[662][85] = 1
adj[85][686] = 1   adj[686][85] = 1
adj[85][727] = 1   adj[727][85] = 1
adj[85][1486] = 1   adj[1486][85] = 1
adj[85][1615] = 1   adj[1615][85] = 1
adj[85][2148] = 1   adj[2148][85] = 1
adj[85][2184] = 1   adj[2184][85] = 1
adj[85][2189] = 1   adj[2189][85] = 1
adj[85][2190] = 1   adj[2190][85] = 1
adj[85][2211] = 1   adj[2211][85] = 1
adj[85][3215] = 1   adj[3215][85] = 1
adj[85][4583] = 1   adj[4583][85] = 1
adj[85][4585] = 1   adj[4585][85] = 1
adj[85][4586] = 1   adj[4586][85] = 1
adj[85][4589] = 1   adj[4589][85] = 1
adj[85][4590] = 1   adj[4590][85] = 1
Segmentation fault (core dumped)

这里有什么问题。请帮忙......

6 个答案:

答案 0 :(得分:2)

问题必须来自内存分配。在经典计算机上,sizeof(int)为4,sizeof(int*)可以是4(32位OS)或8(64位OS)。

在那里,你首先为50000个指针分配空间,因此至少50000 * 4 = 200000个字节。

然后,循环执行此操作以分配50.000 * 50.000 * 4 = 10.000.000.000字节= 10 GB!

由于您 NOT 检查malloc()返回值,我的猜测是在此循环的某个时刻:

for(i=0;i<MAX;++i)
    adj[i] = (int *)malloc(sizeof(int)*MAX);

malloc始终返回NULL。设表示M这样的指数。在你的情况下,我可以猜测M≥4591。

稍后,当您从文件中读取数据时,如果M≤NULL,则会尝试访问a.node1指针。

顺便说一句,您可以像这样分配2D数组:

int **array, i;
if(NULL == (array = malloc(sizeof(int*)*MAX))) {
    printf("Oops, not enough memory ...\n");
    return EXIT_FAILURE;
}
if(NULL == (array[0] = malloc(sizeof(int)*MAX*MAX))) {
    printf("Oops, not enough memory ...\n");
    free(array);
    return EXIT_FAILURE;
}
for(i = 1; i < MAX; i++)
    array[i] = array[0]+i;
// At this point, array is ready to use.
do_stuff();
// When you are done, freeing the memory is not tiresome :
free(array[0]);
free(array);

(请注意,在C中,您永远不会转换malloc的返回。)

这个分配和你的分配有什么区别?在你的,每个adj[i]指向一个动态分配的数据块。因此,这些数据块几乎不可能在内存中连续存在。在我提议的那个中,只有2个内存分配,最后adj[i]adj[i+1]指向的数据块是连续的。

注意:

  

大图的邻接矩阵

虽然邻接矩阵是一种在内存中存储图形的完全有效的方法,但是当图形往往很大时,你应该使用邻接列表。

答案 1 :(得分:1)

50000 * 50000整数非常多。即,4字节整数是9Gb内存。你确定你分配了所有的内存吗?

添加支票:

if (!adj[i])
   return 2;

请注意,您为x64编译并在x64计算机上运行以使其正常工作。很可能你不需要那么多数据。

答案 2 :(得分:0)

在您的特定情况下,不需要分配指向int数组的指针数组。只需分配一个大小为MAX * MAX的单个数组。

答案 3 :(得分:0)

首先,在错误之前添加调试printf

  while(fscanf(ptr_file1,"%d %d",&a.node1, &a.node2)!=EOF)
  {
     printf("%d %d\n", a.node1, a.node2);

     adj[a.node1][a.node2] = 1;                   //Getting segmentation fault here   
     adj[a.node2][a.node1] = 1; 
  }

这样,您可以在程序崩溃之前查看数组索引是否超出范围。

这只是用于调试目的的快速修复 - 实际上你应该进行适当的错误检查:

  while(fscanf(ptr_file1,"%d %d",&a.node1, &a.node2)!=EOF)
  {
     if (a.node1 >= MAX || a.node2 >= MAX)
     {
         fprintf(stderr, "range error: a.node1 = %d, a.node2 = %d\n", a.node1, a.node2);
         exit(1);
     }

     adj[a.node1][a.node2] = 1;                   //Getting segmentation fault here   
     adj[a.node2][a.node1] = 1; 
  }

答案 4 :(得分:0)

发表评论。使用一个维度位图,但一个维度可以用作二维,可用于图形

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

#define MAX 4000000

unsigned char *bitmapinit(int n);
unsigned char chkbit(unsigned char *map, int n);
void setbit(unsigned char *map, int n);
void unsetbit(unsigned char *map, int n);

int main(int argc, char *argv[])
{
        unsigned int i;
        unsigned char *bitmap = bitmapinit(MAX);
        if (!bitmap) {
                perror("malloc: ");
                exit(EXIT_FAILURE);
        }
        for (i = 0; i < MAX; i++) {
                setbit(bitmap, i);
        }
        for (i = 0; i < MAX; i += 5) {
                 unsetbit(bitmap, i);
        }
        for (i = 0; i < MAX; i++) {
                printf("bit #%d = %d\n", i, (chkbit(bitmap, i))?1:0);
        }
        return 0;
}
unsigned char *bitmapinit(int n)
{
        return calloc(sizeof(unsigned char), n / 8 + 1);
}
unsigned char chkbit(unsigned char *map, int n)
{
        return (unsigned char)map[n / 8] & (1 << (n % 8));
}
void setbit(unsigned char *map, int n)
{
        map[n / 8] = map[n / 8] | (1 << (n % 8));
}
void unsetbit(unsigned char *map, int n)
{
        map[n / 8] = map[n / 8] & ~(1 << (n % 8));
}

如果需要,我可以解释它是如何用于图表的。

节省空间8倍。对于50000 x 50000的矩阵,你需要~300MB,图形可以是方向的,但不是多重链接的

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>    
#include <errno.h>

#define ROW 50
#define COL 55

unsigned int *bitmapinit(int, int);
bool chkbit(unsigned int *, int, int, int);
void setbit(unsigned int *, int, int, int);
void unsetbit(unsigned int *, int, int, int);


int main(int argc, char *argv[])
{
    unsigned int i, j;
    unsigned int *bitmap = bitmapinit(ROW, COL);
    if (!bitmap) {
        perror("malloc: ");
        exit(EXIT_FAILURE);
    }
    for (i = 0; i < ROW; i+=2)
        for (j = 0; j < COL; j+=2)
            setbit(bitmap, i, j, COL);    

    for (i = 0; i < ROW; i++) {
        for (j = 0; j < COL; j++) {
            printf("%d ",(chkbit(bitmap, i, j, COL)) ? 1 : 0);
        }
        printf("\n");
    }
    printf("\n");
    for (i = 0; i < ROW; i++)
        for (j = 0; j < COL; j++)
            setbit(bitmap, i, j, COL);

    for (i = 0; i < ROW; i += 3)
        for (j = 0; j < COL; j += 3)
            unsetbit(bitmap, i, j, COL);    

    for (i = 0; i < ROW; i++) {
        for (j = 0; j < COL; j++) {
            printf("%d ",(chkbit(bitmap, i, j, COL)) ? 1 : 0);
        }
        printf("\n");
    }
    return 0;
}

unsigned int *bitmapinit(int row, int col) //n it is ROWS, m it is COLUMNS
{
    return calloc(sizeof(unsigned int), (row * col) / 32 + 1);
}
bool chkbit(unsigned int *map, int row, int col, int n)
{
    return map[(row * n + col) / 32] & (1 << (row * n + col) % 32);
}
void setbit(unsigned int *map, int row, int col, int n)
{
    map[(row * n + col) / 32] = map[(row * n + col) / 32] | (1 << (row * n + col) % 32);
}
void unsetbit(unsigned int *map, int row, int col, int n)
{
    map[(row * n + col) / 32] = map[(row * n + col) / 32] & ~(1 << (row * n + col) % 32);
}

程序并不复杂,实际上它是一个二维数组,但是数组的每个元素都可以设置为只有0或1

但值50000 * 50000可以使用很长时间

分别设置需要调用setbit(unsigned char *map, int Y, int X, int LenOfRow);的XY位 清除XY unsetbit(unsigned char *map, int Y, int X, int LenOfRow);位 并获得位XY checkbit(unsigned char *map, int Y, int X, int LenOfRow);

的值

再次提醒您LenOfRow的值不应在一个数组中更改

答案 5 :(得分:0)

正如其他人所说,你的问题很可能是你的2D阵列的庞大规模。所以你有三个选择:

  1. 优化邻接矩阵的大小。您可以使用int8_t而不是int将内存消耗减少四分之一(在大多数系统上)。您可以使用构成矩阵的整数的各个位将其减去另一个因子8。这是32的因素,足以让您的矩阵降低到可管理的大小。

    您可以使用以下访问器:

    void setAdjacent(int32_t** matrix, int x, int y) {
        matrix[x][y/32] |= (1 << (y & 31));
    }
    
    int isAdjacent(int32_t** matrix, int x, int y) {
        return matrix[x][y/32] & (1 << (y & 31));
    }
    
  2. 利用邻接矩阵稀疏的事实。对于每个节点,存储与其相邻的所有其他节点的列表。

  3. 购买更多内存。


  4. 你也可以像这样使用真正的2D数组:

    int32_t (*matrix)[MAX] = malloc(MAX*sizeof(*matrix));
    

    这避免了为每一行分配数组的麻烦,并避免了一个指针间接的开销。您只需要相应地更改访问者的签名,其内容根本不会改变。