2010-05-24 1 views
8

Ok la mia C è un po 'arrugginita, ma ho pensato che avrei realizzato il mio prossimo (piccolo) progetto in C in modo da poter eseguire il lucido su di esso e meno di 20 righe in Ho già un difetto di seg.casting char [] [] su char ** causa segfault?

Questo è il mio codice completo:

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

Sono completamente confuso su come questo sta causando un segfault. Cosa sta succedendo quando si trasmette da [][] a **!? Questo è l'unico avvertimento che ricevo.

 
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]’ 

Sono [][] e ** davvero non tipi di puntatore compatibili? Sembra che siano solo sintassi per me.

+2

"Tipi di puntatori non compatibili"? Cosa intendi? Il tipo '[] []' è un tipo * array *, non un tipo di puntatore. Perché ti stai riferendo a '[] []' come un tipo di puntatore ??? – AnT

+0

@Andrey è un grosso e ovvio divario nella mia conoscenza. Comprendo perfettamente i puntatori ma non gli array. :) – Earlz

risposta

35

A char[ROWS][COLS+1] non è possibile eseguire il cast in un char**. L'argomento ingresso print_map dovrebbe essere

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

o

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

la differenza che un char** significa puntare qualcosa che può essere dereference simili:

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

Mentre un char(*)[n] è un punto per un regio della memoria continua n Ti piace questa

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

Se si tratta di un (char(*)[5]) come (char**) si ottiene spazzatura:

(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

Questo è un po 'brutto però:/o forse sono solo viziato da altre lingue .. – Earlz

+0

Grazie per aver aggiornato la tua domanda con una spiegazione del perché :) Ora sono confuso per quale risposta è meglio se – Earlz

+6

+1 per la carina arte ascii e il puntatore all'array. –

1

Guardando il mio codice mi sono reso conto che la quantità di colonne è costante, ma in realtà non importa perché è solo una stringa. Così l'ho cambiato così main_map è una matrice di stringhe (er, puntatori di caratteri). Questo lo rende così posso solo usare ** per il passaggio in giro anche:

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

Quando si esegue questa dichiarazione:

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

Si crea un array di array--of-char. Un array di caratteri è solo un blocco di caratteri e un array di array è solo un blocco di array, quindi nel complesso, main_map è solo un mucchio di caratteri. Ecco come si presenta:

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

Quando si passa main_map-print_map(), si sta valutando main_map come un puntatore al primo elemento della matrice - quindi questo indicatore è puntato verso l'inizio di quel blocco di memoria. Si forza il compilatore a convertire questo per digitare char **.

Quando si valuta map[0] all'interno della funzione (ad esempio per la prima iterazione del ciclo), recupera il valore char * indicato da map.Sfortunatamente, come potete vedere dall'arte ASCII, mapnon è punto a char * - punta a un mucchio di semplici char s. Non ci sono valori char *. A questo punto, carichi alcuni di quei valori char (4, o 8 o un altro numero a seconda di quanto grande è il char * sulla tua piattaforma) e prova ad interpretarli come un valore char *.

Quando puts() tenta quindi di seguire il valore falso char *, si ottiene l'errore di segmentazione.

+0

Questo era quello che volevo. Era una spiegazione non solo una soluzione rapida. Grazie per quello :) – Earlz