Une idée fausse très répandue chez les débutants en C est qu'ils supposent que les pointeurs et les tableaux sont équivalents. C'est totalement faux.
La confusion vient aux débutants quand ils voient le code comme
int a1[] = {1,2,3,4,5};
int *p1 = a1; // Beginners intuition: If 'p1' is a pointer and 'a1' can be assigned
// to it then arrays are pointers and pointers are arrays.
p1[1] = 0; // Oh! I was right
a1[3] = 0; // Bruce Wayne is the Batman! Yeah.
Maintenant, les débutants ont vérifié que les tableaux sont des pointeurs et que les pointeurs sont des tableaux, ils font donc de telles expériences :
int a2[][5] = {{0}};
int **p2 = a2;
Et puis un avertissement s'affiche à propos d'une affectation de pointeur incompatible et ils pensent : "Oh mon Dieu ! Pourquoi ce tableau est-il devenu Harvey Dent ?".
Certains ont même une longueur d'avance
int a3[][5][10] = {{{0}}};
int ***p3 = a3; // "?"
et ensuite Riddler vient à leur cauchemar de l'équivalence tableau-pointeur.
N'oubliez jamais que les tableaux ne sont pas des pointeurs et vice-versa. Un tableau est un type de données et un pointeur est un autre type de données ( qui n'est pas de type tableau ). Ce problème a été abordé il y a plusieurs années dans le C-FAQ :
Dire que les tableaux et les pointeurs sont "équivalents" ne signifie pas qu'ils sont identiques ni même interchangeables. Ce que cela signifie, c'est que l'arithmétique des tableaux et des pointeurs est définie de telle sorte qu'un pointeur peut être utilisé de manière pratique pour accéder à un tableau ou pour simuler un tableau. En d'autres termes, comme l'a dit Wayne Throop, c'est "l'arithmétique des pointeurs et l'indexation des tableaux [qui] sont équivalents en C, les pointeurs et les tableaux sont différents". )
Souvenez-vous toujours de quelques règles importantes pour éviter ce genre de confusion :
- Les tableaux ne sont pas des pointeurs. Les pointeurs ne sont pas des tableaux.
- Les tableaux sont convertis en pointeur sur leur premier élément lorsqu'ils sont utilisés dans une expression, sauf lorsqu'un opérande de l'option
sizeof
y &
opérateur.
- C'est le arithmétique des pointeurs y indexation des tableaux qui sont les mêmes.
- Les pointeurs et les tableaux sont différents.
- Ai-je dit "les pointeurs ne sont pas des tableaux et vice-versa".
Maintenant que vous avez les règles, vous pouvez conclure qu'en
int a1[] = {1,2,3,4,5};
int *p1 = a1;
a1
est un tableau et dans la déclaration int *p1 = a1;
il a été converti en pointeur vers son premier élément. Ses éléments sont de type int
alors le pointeur vers son premier élément serait de type int *
qui est compatible avec p1
.
Sur
int a2[][5] = {{0}};
int **p2 = a2;
a2
est un tableau et dans int **p2 = a2;
il se désintègre pour pointer vers son premier élément. Ses éléments sont de type int[5]
(un tableau 2D est un tableau de tableaux 1D), donc un pointeur vers son premier élément serait de type int(*)[5]
(pointeur vers un tableau) qui est incompatible avec le type int **
. Il devrait être
int (*p2)[5] = a2;
De même pour
int a3[][5][10] = {{{0}}};
int ***p3 = a3;
éléments de a3
est de type int [5][10]
et le pointeur vers son premier élément serait de type int (*)[5][10]
mais p3
est de int ***
donc, pour les rendre compatibles, il faut que ce soit
int (*p3)[5][10] = a3;
Venons-en maintenant à votre extrait
int A[5][5];
int B[5][5];
int*** C = {&A, &B};
&A
y &B
sont de type int(*)[5][5]
. C
est de type int***
il ne s'agit pas d'un tableau. Puisque vous voulez faire C
pour contenir l'adresse des deux tableaux A
y B
vous devez déclarer C
comme un tableau de deux int(*)[5][5]
éléments de type. Cela doit être fait comme
int (*C[2])[5][5] = {&A, &B};
Cependant, si j'alloue dynamiquement A et B, cela fonctionne très bien. Comment cela se fait-il ?
Dans ce cas, vous devez avoir déclaré A
y B
como int **
. Dans ce cas, les deux sont des pointeurs, pas des tableaux. C
est de type int ***
Il peut donc contenir une adresse de int**
données de type. Notez que dans ce cas, la déclaration int*** C = {&A, &B};
devrait être
int*** C = &A;
En cas de int*** C = {&A, &B};
le comportement du programme serait soit indéfini, soit défini par l'implémentation.
C11 : 5.1.1.3 (P1) :
Une implémentation conforme doit produire au moins un message de diagnostic (identifié d'une manière définie par l'implémentation) si une unité de traduction de prétraitement ou une unité de traduction contient une violation de toute règle ou contrainte syntaxique, même si le comportement est aussi explicitement spécifié comme non défini ou défini par l'implémentation.
Leer <a href="https://stackoverflow.com/q/28412577/2455888">ce poste </a>pour plus d'explications.
5 votes
Conseil : vérifiez les types de données
3 votes
"si je déclare A et B comme tableau dynamique...". C n'a pas cette fonctionnalité. Vous ne pouvez pas "déclarer A et B comme des tableaux dynamiques". Vous pouvez seulement les déclarer comme des pointeurs et ensuite manuellement allouer de la mémoire pour les faire se comporter comme des tableaux multidimensionnels dynamiques. Ces tableaux "construits manuellement" ne sont pas compatibles avec les tableaux multidimensionnels intégrés - ce sont deux choses complètement différentes.
0 votes
Ce code produit de l'UB. "L'initialisateur d'un scalaire doit être une expression unique, éventuellement entourée d'accolades." GCC et clang acceptent beaucoup de code merdique (comme une extension ?). Compiler en utilisant msvc produira une erreur (vie exemple ).
0 votes
Dites juste var C={&A, &B}, Oh, attendez, c'est C-quoi ?