34 votes

Pourquoi int*[] se décompose-t-il en int** mais pas int[][] ?

J'essaie de comprendre la nature de la désintégration des types. Par exemple, nous savons tous que les tableaux se décomposent en pointeurs dans un certain contexte. Ma tentative est de comprendre comment int[] équivaut à int* mais comment les tableaux à deux dimensions ne correspondent pas au type de pointeur attendu. Voici un cas de test :

std::is_same<int*, std::decay<int[]>::type>::value; // true

Ceci renvoie vrai comme prévu, mais ceci ne le fait pas :

std::is_same<int**, std::decay<int[][1]>::type>::value; // false

Pourquoi est-ce que ce n'est pas vrai ? J'ai finalement trouvé un moyen de lui faire retourner la vérité, en faisant de la première dimension un pointeur :

std::is_same<int**, std::decay<int*[]>::type>::value; // true

Et l'affirmation est vraie pour tout type avec des pointeurs mais avec le dernier étant le tableau. Par exemple ( int***[] == int****; // true ).

Puis-je avoir une explication sur la raison pour laquelle cela se produit ? Pourquoi les types de tableaux ne correspondent-ils pas aux types de pointeurs comme on pourrait s'y attendre ?

62voto

Oli Charlesworth Points 148744

¿Por qué int*[] se dégrader en int** mais pas int[][] ?

Parce qu'il serait impossible de faire de l'arithmétique de pointeur avec.

Par exemple, int p[5][4] signifie un tableau de (longueur-4 tableau de int ). Aucun pointeur n'est impliqué, il s'agit simplement d'un bloc de mémoire contigu de taille 5*4*sizeof(int) . Lorsque vous demandez un élément particulier, par ex. int a = p[i][j] le compilateur fait vraiment cela :

char *tmp = (char *)p           // Work in units of bytes (char)
          + i * sizeof(int[4])  // Offset for outer dimension (int[4] is a type)
          + j * sizeof(int);    // Offset for inner dimension
int a = *(int *)tmp;            // Back to the contained type, and dereference

Évidemment, il ne peut le faire que parce qu'il connaît la taille de la ou des dimensions "intérieures". Le transfert vers un int (*)[4] conserve cette information ; c'est un pointeur vers (tableau de longueur 4 de int ). Cependant, un int ** n'a pas ; il s'agit simplement d'un pointeur vers (pointeur vers int ).

Pour un autre point de vue, voir les sections suivantes de la FAQ C :

(Tout ceci est pour le C, mais ce comportement est essentiellement inchangé en C++).

10voto

supercat Points 25534

Le langage C n'a pas vraiment été "conçu" ; au contraire, des fonctionnalités ont été ajoutées au fur et à mesure des besoins, en s'efforçant de ne pas casser le code antérieur. Une telle approche évolutive était une bonne chose à l'époque où le C était en cours de développement, car cela signifiait que, pour la plupart, les développeurs pouvaient récolter les avantages des premières améliorations du langage avant que tout ce que le langage pourrait avoir besoin de faire ne soit élaboré. Malheureusement, la façon dont la gestion des tableaux et des pointeurs a évolué a conduit à une variété de règles qui, rétrospectivement, sont malheureuses.

Dans le langage C d'aujourd'hui, il existe un système de types assez substantiel, et les variables ont des types clairement définis, mais les choses n'ont pas toujours été ainsi. Une déclaration char arr[8] ; allouerait 8 octets dans l'étendue actuelle, et rendrait arr pointe vers le premier d'entre eux. Le compilateur ne saurait pas que arr représentait un tableau - il représenterait un pointeur de caractère comme n'importe quel autre tableau. char* . D'après ce que je comprends, si on avait déclaré char arr1[8], arr2[8]; la déclaration arr1 = arr2; aurait été parfaitement légal, étant quelque peu équivalent conceptuellement à char *st1 = "foo, *st2 = "bar"; st1 = st2; mais aurait presque toujours représenté un bug.

La règle selon laquelle les tableaux se décomposent en pointeurs provient d'une époque où les tableaux et les pointeurs étaient vraiment la même chose. Depuis lors, les tableaux ont été reconnus comme un type distinct, mais le langage devait rester essentiellement compatible avec l'époque où ils ne l'étaient pas. Lorsque les règles ont été formulées, la question de la gestion des tableaux bidimensionnels ne se posait pas, car ils n'existaient pas. On pouvait faire quelque chose comme char foo[20]; char *bar[4]; int i; for (i=0; i<4; i++) bar[i] = foo + (i*5); et ensuite utiliser bar[x][y] de la même manière qu'on utiliserait aujourd'hui un tableau à deux dimensions, mais un compilateur ne verrait pas les choses de cette manière - il verrait simplement bar comme un pointeur vers un pointeur. Si l'on voulait que foo[1] pointe à un endroit complètement différent de foo[2], on pourrait parfaitement le faire légalement.

Lorsque les tableaux à deux dimensions ont été ajoutés au C, il n'était pas nécessaire de maintenir la compatibilité avec le code antérieur qui déclarait les tableaux à deux dimensions, car il n'y en avait pas. Alors qu'il aurait été possible de spécifier que char bar[4][5]; générerait un code équivalent à ce qui a été montré en utilisant l'option foo[20] Dans ce cas, une char[][] aurait été utilisable en tant que char** On a pensé que, de la même manière que l'assignation de variables de tableau aurait été une erreur dans 99% des cas, il en aurait été de même pour la réassignation de lignes de tableau, si cela avait été légal. Ainsi, les tableaux en C sont reconnus comme des types distincts, avec leurs propres règles qui sont un peu étranges, mais qui sont ce qu'elles sont.

7voto

Nawaz Points 148870

Parce que int[M][N] y int** sont des types incompatibles.

Cependant, int[M][N] peut se décomposer en int (*)[N] type. Ainsi, les éléments suivants :

std::is_same<int(*)[1], std::decay<int[1][1]>::type>::value;

devrait vous donner true .

3voto

Dave Hillier Points 4391

Les tableaux à deux dimensions ne sont pas stockés comme des pointeurs sur des pointeurs, mais comme un bloc de mémoire contigu.

Un objet déclaré comme type int[y][x] est un bloc de taille sizeof(int) * x * y alors qu'un objet de type int ** est un pointeur vers un int*

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X