Pourquoi cela génère-t-il une erreur de segmentation?

#include void foo(int **arr) { arr[1][1]++; } main() { int arr[20][20]; printf("%d\n",arr[1][1]); foo((int**)arr); printf("%d\n",arr[1][1]); } 

Supposons que vous déclariez: int arr [10] [20];
Quel type est arr?
Vous pensez peut-être que c’est int ** , mais c’est inexact.

C’est en fait de type int (*)[20] lorsqu’il se désintègre (comme lorsque vous le transmettez à une fonction);
Un tableau en décomposition ne s’applique qu’une seule fois.

Détails ici


Considérons maintenant ce qui suit,

 #include #include void foo(int arr[][20]) { arr[1][1]++; } main() { int (*arr)[20]; arr = malloc(sizeof(int (*)[]) * 2); //2 rows & malloc will do implicit cast. printf("%d\n",arr[1][1]); foo(arr); printf("%d\n",arr[1][1]); } 

Sortie:

$ gcc fdsf.c && ./a.out
0
1


arr et arr + 1 pointent vers un tableau de 20 entiers.

arr + 0 -> int int int … int (20 ints, contigu)
[0] [0] [0] [1]
arr + 1 -> int int int … int (20 ints, contigus)
[1] [0] [1] [1]

Voici à quoi ressemble un int[2][2] en mémoire:

int[2] int[2]

C’est-à-dire un tableau immédiatement suivi d’un autre tableau.

Voici à quoi ressemble un int[2] en mémoire:

int int

C’est-à-dire, un int immédiatement suivi d’un autre int.

Donc, voici aussi à quoi ressemble un int[2][2] en mémoire:

 int int int int ^ ^ | |___ this is arr[1][1] | |____ this is p[1], assuming sizeof(int*) == sizeof(int) 

Si vous lancez arr sur int** , je vais appeler le résultat p . Ensuite, il pointe vers la même mémoire. Quand vous faites p[1][1] vous n’obtenez pas arr[1][1] . Au lieu de cela, le programme lit la valeur en p[1] , l’ajuste par la taille d’un int et le déréférence . Si cette seconde int contenait, par exemple, la valeur “21”, alors vous venez d’essayer de déréférencer le pointeur “25” (si int est de 4 octets). Ce n’est pas bien.

Les tableaux ne sont pas identiques aux pointeurs, et les tableaux à deux dimensions ne sont certainement pas la même chose que les pointeurs à pointeurs.

Parce que foo attend un pointeur sur un pointeur sur int et que vous lui transmettez un pointeur sur un tableau de 20 int. Le lancer ne changera pas le fait que ce n’est pas le bon type.

Si vous le changez comme ceci, vous obtenez le résultat attendu:

 #include void foo(int arr[][20]) { arr[1][1]++; } int main() { int arr[20][20]; arr[1][1] = 1; printf("%d\n",arr[1][1]); foo(arr); printf("%d\n",arr[1][1]); } 

foo besoin de connaître la taille du tableau (enfin, au moins la deuxième dimension du tableau n’est pas nécessaire en premier), sinon il ne peut pas effectuer l’arithmétique de pointeur nécessaire pour [1][1] .

Le problème est que int arr[20][20] pour tableau 2D signifie que ce tableau est stocké en tant que tableau 1d et que les lignes sont stockées les unes après les autres. lorsque vous indexez sur int **arr vous prenez en fait le deuxième élément de la première ligne du tableau, puis vous le déréférence et le premier élément à cet endroit.