casting char to char ** provoque une erreur de segmentation?

Ok, mon C est un peu rouillé mais je me suis dit que je ferais mon prochain (petit) projet en C pour pouvoir le retoucher et qu’il rest moins de 20 lignes sur un autre.

Ceci est mon code complet:

#define ROWS 4 #define COLS 4 char main_map[ROWS][COLS+1]={ "a.bb", "ac", "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 du casting de [][] à ** !? C’est le seul avertissement que je reçois.

 rushhour.c: 23: 3: avertissement: passage de l'argument 1 de 'print_map' à partir d'un type de pointeur incompatible
 rushhour.c: 13: 7: note: 'char **' attendu mais l'argument est de type 'char (*) [5]'

Les types de pointeurs [][] et ** ne sont-ils vraiment pas compatibles? Ils semblent être juste la syntaxe pour moi.

Un caractère char[ROWS][COLS+1] ne peut pas être converti en un caractère char** . L’argument d’entrée de print_map devrait être

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

ou

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

La différence est qu’un caractère char** signifie pointer sur 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 caractère char(*)[n] est un pointeur sur une région de mémoire continue comme ceci

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

Si vous traitez un (char(*)[5]) comme un (char**) vous obtenez des déchets:

  (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 +---------------+ | | vv none of your process memory SEGFAULT 

Lorsque vous faites cette déclaration:

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

Vous créez un tableau de tableaux de caractères. Un tableau de caractères est juste un bloc de caractères, et un tableau de tableaux est juste un bloc de tableaux – ainsi, dans l’ensemble, main_map n’est qu’un tas de caractères. Cela ressemble à ceci:

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

Lorsque vous passez main_map à print_map() , il évalue main_map tant que pointeur sur le premier élément du tableau. Ce pointeur pointe donc au début de ce bloc de mémoire. Vous forcez le compilateur à convertir ceci en type char ** .

Lorsque vous évaluez map[0] dans la fonction (par exemple, pour la première itération de la boucle), la valeur char * indiquée par map . Malheureusement, comme vous pouvez le constater dans ASCII-art, map ne pointe pas sur un caractère char * , mais sur un groupe de caractères simples. Il n’y a pas de valeurs char * du tout. À ce stade, vous chargez certaines de ces valeurs de caractère (4, 8 ou un autre nombre en fonction de la taille de votre caractère char * sur votre plate-forme) et essayez de les interpréter comme une valeur car char * .

Quand puts() essaie alors de suivre cette fausse valeur char * , vous obtenez votre erreur de segmentation.

En regardant mon code, j’ai réalisé que le nombre de colonnes est constant, mais cela n’a pas d’importance, car il s’agit simplement d’une chaîne. Donc, je l’ai changé pour que main_map soit un tableau de chaînes (er, pointeurs de caractères). Cela fait que je peux simplement utiliser ** pour le transmettre également:

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