簡體   English   中英

使用malloc為2D數組獲取分段錯誤

[英]Getting segmentation fault using malloc for 2D array

我已經使用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)

這是怎么了,請幫助...

問題一定來自內存分配。 在經典計算機上, 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!

由於您檢查malloc()返回值,我的猜測是在此循環的某個時刻:

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

malloc始終返回NULL 讓M表示這樣的索引。 在您的情況下,我可以猜測M≥4591。

稍后,當從文件中讀取數據時,如果a.node1 ,則嘗試訪問NULL指針。

順便說一下,您可以像這樣分配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]指向的數據塊是連續的。

注意:

大圖的鄰接矩陣

盡管鄰接矩陣是將圖形存儲在內存中的一種完全有效的方法,但是當圖形往往很大時,您應該使用鄰接列表。

50000 * 50000整數是很多。 即,它是4字節整數的9Gb內存。 您確定要分配所有內存嗎?

添加支票:

if (!adj[i])
   return 2;

請注意,您必須為x64編譯並在x64機器上運行才能運行。 最有可能您不需要那么多數據。

在您的特定情況下,無需分配指向整數數組的指針數組。 只需分配一個整數數組,其大小為MAX * MAX。

首先,在錯誤之前添加一個調試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; 
  }

征求意見。 使用一維位圖,但一維可以用作二維空間,對圖形很有用

#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可以長時間工作

要分別設置XY位,您需要調用setbit(unsigned char *map, int Y, int X, int LenOfRow); 清除位XY unsetbit(unsigned char *map, int Y, int X, int LenOfRow); 並獲取XY校驗位的值checkbit(unsigned char *map, int Y, int X, int LenOfRow);

再次提醒您, LenOfRow的值不應在一個數組中更改

正如其他人指出的那樣,您的問題很可能是2D陣列的絕對大小。 因此,您有三種選擇:

  1. 優化鄰接矩陣的大小。 使用int8_t而不是int可以將內存消耗減少四倍(在大多數系統上)。 您可以使用構成矩陣的整數的各個位將其減少八分之一。 這是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. 購買更多RAM。


您也可以像這樣使用真正的2D數組:

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

這避免了為每行分配數組的麻煩,並且避免了一個指針間接的開銷。 您只需要相應地更改訪問器的簽名,它們的內容就不會改變。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM