2010-05-24 10 views
8

Ok, mon C est un peu rouillé mais je me suis dit que je ferais mon prochain (petit) projet en C pour que je puisse polir en arrière et moins de 20 lignes en j'ai déjà une faute de seg.La conversion de char [] [] en caractère char ** provoque un segfault?

Ceci est mon code complet:

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

Je suis complètement confus quant à la façon dont cela provoque une erreur de segmentation. Que se passe-t-il lors de la diffusion de [][] à **!? C'est le seul avertissement que je reçois.

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

sont-[][] et ** vraiment pas les types de pointeur compatibles? Ils semblent être juste une syntaxe pour moi.

+2

"Types de pointeurs non compatibles"? Que voulez-vous dire? Votre type '[] []' est un type * array *, pas un type de pointeur. Pourquoi faites-vous référence à '[] []' comme type de pointeur ??? – AnT

+0

@Andrey c'est un énorme écart évident dans mes connaissances en C. Je comprends complètement les pointeurs mais pas les tableaux. :) – Earlz

Répondre

35

Un char[ROWS][COLS+1] ne peut pas être converti en char**. L'argument d'entrée de print_map doit être

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

ou

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

La différence étant qu'un char** signifie pour pointer vers quelque chose qui peut être déréférencé comme ceci:

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

Alors qu'un char(*)[n] est un des points à un regio de mémoire continue n comme celui-ci

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

Si vous traitez un (char(*)[5]) comme (char**) vous obtenez des ordures:

(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

C'est un peu moche si:/ou peut-être je suis juste gâté par d'autres langues .. – Earlz

+0

Merci pour la mise à jour de votre question avec une explication de pourquoi :) Maintenant, je suis confus pour quelle réponse est meilleure si – Earlz

+6

+1 pour le mignon ascii-art et le pointeur vers le tableau. –

1

regardant mon code, je compris que la quantité de colonnes est constante, mais il ne fait pas affaire Parce que c'est juste une chaîne. Donc je l'ai changé donc main_map est un tableau de chaînes (er, char pointeurs). Cela rend donc je peux simplement utiliser ** pour passer autour aussi:

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

Quand vous faites cette déclaration:

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

Vous créez un tableau-de-réseaux-de-char. Un tableau-de-caractère est juste un bloc de caractères, et un tableau de tableaux est juste un bloc de tableaux - donc globalement, main_map est juste un tas de caractères. Il ressemble à ceci:

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

Lorsque vous passez main_map-print_map(), il évalue main_map comme un pointeur vers le premier élément du tableau - donc ce pointeur pointe au début de ce bloc de mémoire. Vous forcez le compilateur à convertir cela en type char **.

Lorsque vous évaluez map[0] dans la fonction (par exemple, pour la première itération de la boucle), il récupère la valeur char * pointée par map.Malheureusement, comme vous pouvez le voir à partir de l'ASCII-art, mapne fait pas pointer vers un char * - il pointe à un tas de plaine char s. Il n'y a aucune valeur char *. À ce stade, vous chargez certaines de ces valeurs char (4, ou 8, ou un autre nombre en fonction de la taille de char * sur votre plate-forme) et essayez d'interpréter ces valeurs comme char *.

Lorsque puts() essaie ensuite de suivre cette valeur char * faux, vous obtenez votre erreur de segmentation.

+0

C'était ce que je voulais. Était une explication pas seulement une solution rapide. Merci pour ça :) – Earlz