2010-04-15 9 views
8

Dlaczego następujący kod powoduje błąd segmentacji? (Próbuję utworzyć dwie matryce o tej samej wielkości, jeden z statyczna i inne z dynamicznej alokacji)Alokacja pamięci dla macierzy w 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); 
    } 
} 

wystarczająco Niesamowicie, jeśli I ustosunkowania się jedną z definicji macierzy, kod działa poprawnie. Tak:

#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); 
    } 
} 

lub

#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); 
    //} 
} 

biegnę gcc w systemie Linux na komputerze 32-bitowym.

Edit: sprawdzenie czy malloc() powiedzie:

#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; 
      } 
     } 
    }  
} 

Nic nie jest drukowany, gdy uruchomię go (oczywiście oczekiwać dla "winy segmentacji")

+0

Wypróbuj 'fprintf' na' stderr'. 'printf' drukuje na' stdout', który jest buforowany, więc jeśli program się zawiesza, prawdopodobnie stracisz wydruk. –

+0

Czy możesz również wydrukować i zobaczyć, jak daleko w pętli dostaje się przed niepowodzeniem? –

Odpowiedz

2

Otrzymujesz błąd segmentacji, co oznacza, że ​​twój program próbuje uzyskać dostęp do adresu pamięci, który nie został przypisany do jego procesu. Tablica a jest zmienną lokalną i tym samym przydzielana jest pamięć ze stosu. Jak wskazano, unwind wymaga 120 MB pamięci. Jest to prawie na pewno większe niż przestrzeń stosu, którą system operacyjny przypisał do twojego procesu. Jak tylko pętla for wyjdzie z końca stosu, pojawi się błąd segmentacji.

W Linuksie rozmiar stosu jest kontrolowany przez system operacyjny nie kompilator więc spróbuj wykonać następujące czynności: -

$ ulimit -a 

W odpowiedzi powinieneś zobaczyć linię coś takiego: -

stack size (kbytes)   (-s) 10240 

Oznacza to, że każdy proces otrzymuje 10 MB pamięci, gdzieś w pobliżu wystarczająco dużo dla dużej macierzy.

Możesz dostosować rozmiar stosu za pomocą polecenia ulimit -s <stack size>, ale podejrzewam, że nie pozwoli ci wybrać rozmiaru stosu 120Mb!

Najprostszym rozwiązaniem jest wprowadzenie a jako zmiennej globalnej zamiast zmiennej lokalnej.

+0

Wykonywanie "a" globalnej zmiennej naprawdę rozwiązało problem. Dzięki! (Prawdziwy program, nad którym pracuję jest w rzeczywistości dużo bardziej złożony niż ten zabawkowy przykład, ale ta sama zasada obowiązuje, więc myślę, że teraz mogę do niego wrócić). – Snogzvwtr

1

Są przydziały spore. Czy próbowałeś sprawdzić, aby upewnić się, że malloc() się powiedzie?

Możesz użyć funkcji malloc() dla wszystkich swoich tablic, i sprawdź, czy się uda za każdym razem.

6

Twoja zmienna a wymaga w systemie 32-bitowym 5000 * 6000 * 4 = 120 MB miejsca na stosie. Możliwe, że narusza to pewien limit, który powoduje błąd segmentacji.

Możliwe jest również, że w pewnym momencie w pewnym momencie zawodzi malloc(), co może doprowadzić do usunięcia wskaźnika NULL.

+0

Chociaż o tym, ale nadal nie mogę myśleć z trzecim kodem działa, jeśli tak jest. W każdym razie próbowałem sprawdzić wyniki malloc(), najwidoczniej wszystkie się powiodły. – Snogzvwtr

0

Twój trzeci kod też nie działa (przynajmniej w moim systemie).

Spróbuj przydzielić pamięć do tablicy a raczej na stercie (gdy wymiary są duże).

2

próbować zwiększyć limity sterty i stosu w GCC:

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

Próbowałem, ale dostałem "nierozpoznaną opcję" - "stack". Z tego co sprawdziłem, podobno te opcje są tylko dla Windows. Jestem na Linuksie. – Snogzvwtr

+0

W Linuksie opcje te są dostępne tylko dla celów PE i386. – Juliano

0

Obie macierze nie mieszczą się w granicach swojej pamięci. Możesz przydzielić tylko jeden naraz.

Jeśli zdefiniujesz Y jako 3000 zamiast 6000, Twój program nie powinien wydawać segfault.

+0

Mam (dużo) więcej niż 120 MB dostępnej pamięci. Musi być sposób na robienie tego dobrze. (Właściwie to matryce, których potrzebuję w moim aktualnym programie, są nawet lager - to tylko zabawny przykład, który pomaga zorientować się, co dzieje się źle). – Snogzvwtr

1

Przepełnienie stosu (w razie potrzeby!) Może spowodować błąd segmentacji, który wydaje się być widoczny w tym miejscu.

W trzecim przypadku wskaźnik stosu jest przenoszony na nieprawidłowy adres, ale nie jest używany do niczego, ponieważ program kończy działanie. Jeśli umieścisz dowolną operację po przydzieleniu stosu, powinieneś otrzymać błąd segfault.

1

Być może kompilator po prostu zmienia wskaźnik stosu na pewną dużą wartość, ale nigdy nie używa go, a tym samym nigdy nie powoduje naruszenia dostępu do pamięci.

Spróbuj zainicjować wszystkie elementy A w trzecim przykładzie? Twój pierwszy przykład próbuje przydzielić B po A na stosie, a dostęp do stosu, który jest wysoki (przy pierwszym przyporządkowaniu do B) może być przyczyną tego, co powoduje błąd segfault.

+0

Dzięki, myślę, że teraz rozumiem problem. Nie zdawałem sobie sprawy przed tym, że uzyskanie dostępu do stosu (tworzenie b) * po * przydzieleniu tak dużej ilości miejsca na to, co może spowodować uszkodzenie. – Snogzvwtr