2010-05-24 25 views
8

Ok mój C jest nieco zardzewiały, ale pomyślałem, że zrobię mój następny (mały) projekt w C, więc mogę polerować kopię zapasową na nim i mniej niż 20 linii w już mam wadę seg.odlewanie char [] [] na char ** powoduje uszkodzenie?

To mój kompletny kod:

#define ROWS 4 
#define COLS 4 

char main_map[ROWS][COLS+1]={ 
    "a.bb", 
    "a.c.", 
    "adc.", 
    ".dc."}; 

void print_map(char** map){ 
    int i; 
    for(i=0;i<ROWS;i++){ 
    puts(map[i]); //segfault here 
    } 
} 



int main(){ 
    print_map(main_map); //if I comment out this line it will work. 
    puts(main_map[3]); 
    return 0; 
} 

jestem kompletnie zdezorientowany, jak to jest przyczyną segfault. Co się dzieje podczas przesyłania z [][] do **!? To jest jedyne ostrzeżenie, które otrzymuję.

 
rushhour.c:23:3: warning: passing argument 1 of ‘print_map’ from incompatible pointer type 
rushhour.c:13:7: note: expected ‘char **’ but argument is of type ‘char (*)[5]’ 

Czy [][] i ** naprawdę nie kompatybilne typy wskaźnik? Wydaje się, że są dla mnie tylko składnią.

+2

"Nieodpowiednie typy wskaźników"? Co masz na myśli? Twój typ '[] []' jest typem * array *, a nie wskaźnikiem. Dlaczego mówisz o '[] []' jako typie wskaźnika ??? – AnT

+0

@Andrey to duża, ogromna dziura w mojej wiedzy C. W pełni rozumiem wskaźniki, ale nie tablice. :) – Earlz

Odpowiedz

35

Nie można przenieść pliku char[ROWS][COLS+1] do char**. Argument wejściowy print_map powinny być

void print_map(char map[][COLS+1]) 

lub

void print_map(char (*map)[COLS+1]) 

Różnica jest, że char** znaczy wskazywać na coś, co można dereferencjonowane tak:

(char**)map 
     | 
     v 
    +--------+--------+------+--------+-- ... 
    | 0x1200 | 0x1238 | NULL | 0x1200 | 
    +----|---+----|---+--|---+----|---+-- ... 
     v  |  =  | 
    +-------+ |    | 
    | "foo" | <-----------------' 
    +-------+ | 
       v 
      +---------------+ 
      | "hello world" | 
      +---------------+ 

Podczas gdy char(*)[n] jest punktem do regio pamięci ciągłej n jak to

(char(*)[5])map 
     | 
     v 
    +-----------+---------+---------+-------------+-- ... 
    | "foo\0\0" | "hello" | " worl" | "d\0\0\0\0" | 
    +-----------+---------+---------+-------------+-- ... 

Jeśli traktować (char(*)[5]) jako (char**) dostaniesz śmieci:

(char**)map 
     | 
     v 
    +-----------+---------+---------+-------------+-- ... 
    | "foo\0\0" | "hello" | " worl" | "d\0\0\0\0" | 
    +-----------+---------+---------+-------------+-- ... 
     force cast (char[5]) into (char*): 
    +----------+------------+------------+------------+-- ... 
    | 0x6f6f66 | 0x6c686500 | 0x77206f6c | 0x646c726f | 
    +----|-----+---------|--+------|-----+------|-----+-- ... 
     v    |   |   | 
    +---------------+ |   |   v 
    | "hsd®yœâñ~22" | |   |  launch a missile 
    +---------------+ |   | 
         v   v 
       none of your process memory 
         SEGFAULT 
+0

To trochę brzydkie:/lub może jestem po prostu zepsuta przez inne języki .. – Earlz

+0

Dziękuję za aktualizację pytania z wyjaśnieniem, dlaczego :) Teraz jestem zdezorientowany, dla której odpowiedź jest lepsza choć – Earlz

+6

+1 dla śliczna ascii-art i wskaźnik do tablicy. –

1

Patrząc na mojego kodu zdałem sobie sprawę, że ilość kolumn jest stała, ale faktycznie nie sprawa bo to tylko ciąg znaków. Więc zmieniłem to tak, że main_map to tablica ciągów znaków (er, char pointers). To sprawia, że ​​tak można po prostu użyć ** za przepuszczenie go dookoła również:

char *main_map[ROWS]={ 
    "a.bb", 
    "a.c.", 
    "adc.", 
    ".dc."}; 
3

Po wykonaniu tej deklaracji:

char main_map[ROWS][COLS+1]={ 
    "a.bb", 
    "a.c.", 
    "adc.", 
    ".dc."}; 

utworzyć tablicę-of-tablice-of-char. Tablica znaków jest po prostu blokiem znaków, a tablica-tablic jest po prostu blokiem tablic - więc ogólnie rzecz biorąc, main_map jest po prostu całą masą znaków. Wygląda to tak:

| 'a' | '.' | 'b' | 'b' | 0 | 'a' | '.' | 'c' | '.' | 0 | ... | 'd' | 'c' | '.' | 0 | 

Po przejechaniu main_map do print_map(), to jest ocena main_map jako wskaźnik do pierwszego elementu tablicy - więc ten wskaźnik wskazuje na początku tego bloku pamięci. Zmuszasz kompilator do przekonwertowania tego na typ char **.

Podczas oceny map[0] wewnątrz funkcji (np. Dla pierwszej iteracji pętli), to pobiera wartość char * wskazywanego przez map.Niestety, jak widać z ASCII-art, mapnie oznacza, że ​​ wskazuje na char * - wskazuje na kilka zwykłych s. char. Tam w ogóle nie ma wartości char *. W tym momencie ładujesz niektóre z tych wartości char (4 lub 8 lub inne liczby w zależności od tego, jak duża char * jest na twojej platformie) i próbujesz je interpretować jako wartość char *.

Gdy puts() następnie próbuje podążać za tą fałszywą wartością char *, otrzymasz błąd segmentacji.

+0

Tego właśnie chciałem. Czy wyjaśnienie nie było tylko szybką naprawą. Dziękuję za to :) – Earlz