419 votes

Qu'est-ce que la décroissance du tableau par rapport au pointeur ?

Qu'est-ce que la décroissance du tableau par rapport au pointeur ? Y a-t-il un rapport avec les pointeurs de tableau ?

80 votes

Peu connu : L'opérateur unaire plus peut être utilisé comme un "opérateur de désintégration" : Étant donné int a[10]; int b(void); entonces +a est un pointeur int et +b est un pointeur de fonction. Utile si vous voulez le passer à un modèle acceptant une référence.

3 votes

@litb - les parenthèses feraient la même chose (par exemple, (a) devrait être une expression qui évalue un pointeur), non ?

25 votes

std::decay de C++14 serait une façon moins obscure de décomposer un tableau sur des + unaires.

307voto

phoebus Points 7358

On dit que les tableaux se "décomposent" en pointeurs. Un tableau C++ déclaré comme int numbers [5] ne peut pas être recollée, c'est-à-dire que vous ne pouvez pas dire numbers = 0x5a5aff23 . Plus important encore, le terme "décadence" signifie la perte de type et de dimension ; numbers se dégrader en int* en perdant l'information sur la dimension (compte 5) et le type n'est pas int [5] plus. Regardez ici pour les cas où la dégradation ne se produit pas .

Si vous passez un tableau par valeur, ce que vous faites réellement est de copier un pointeur - un pointeur sur le premier élément du tableau est copié sur le paramètre (dont le type devrait également être un pointeur sur le type de l'élément du tableau). Cela fonctionne grâce à la nature décroissante du tableau ; une fois décroissant, sizeof ne donne plus la taille complète du tableau, car elle devient essentiellement un pointeur. C'est pourquoi il est préférable (entre autres raisons) de passer par référence ou par pointeur.

Trois façons de passer dans un tableau 1 :

void by_value(const T* array)   // const T array[] means the same
void by_pointer(const T (*array)[U])
void by_reference(const T (&array)[U])

Les deux derniers donneront une bonne sizeof info, alors que la première ne le fera pas puisque l'argument du tableau s'est décomposé pour être assigné au paramètre.

1 La constante U doit être connue au moment de la compilation.

9 votes

Comment le premier passe-t-il par valeur ?

11 votes

By_value est le passage d'un pointeur vers le premier élément du tableau ; dans le contexte des paramètres de fonction, T a[] est identique à T *a . by_pointer fait passer la même chose, sauf que la valeur du pointeur est maintenant qualifiée const . Si vous voulez passer un pointeur au tableau (par opposition à un pointeur vers le premier élément du tableau), la syntaxe est la suivante T (*array)[U] .

4 votes

"avec un pointeur explicite vers ce tableau" - c'est incorrect. Si a est un tableau de char entonces a est de type char[N] et se décomposera en char* mais &a est de type char(*)[N] et sera no la décomposition.

109voto

system PAUSE Points 10051

Les tableaux sont fondamentalement les mêmes que les pointeurs en C/C++, mais pas tout à fait. Une fois que vous avez converti un tableau :

const int a[] = { 2, 3, 5, 7, 11 };

en un pointeur (ce qui fonctionne sans casting, et peut donc se produire de manière inattendue dans certains cas) :

const int* p = a;

vous perdez la capacité de la sizeof pour compter les éléments du tableau :

assert( sizeof(p) != sizeof(a) );  // sizes are not equal

Cette perte de capacité est appelée "déchéance".

Pour plus de détails, consultez ce document article sur la dégradation du tableau .

56 votes

Les tableaux sont no sont fondamentalement les mêmes que les pointeurs ; ce sont des animaux complètement différents. Dans la plupart des contextes, un tableau peut être traité comme suit comme si il s'agissait d'un pointeur, et un pointeur peut être traité comme si c'était un tableau, mais c'est le plus proche qu'ils obtiennent.

21 votes

John, veuillez pardonner l'imprécision de mon langage. J'essayais de trouver la réponse sans m'enliser dans une longue histoire, et "en gros... mais pas tout à fait" est la meilleure explication que j'ai pu obtenir à l'université. Je suis sûr que toute personne intéressée peut se faire une idée plus précise en lisant votre commentaire.

1 votes

"fonctionne sans casting" signifie la même chose que "se produit implicitement" lorsqu'on parle de conversions de type.

52voto

Michael Burr Points 181287

Voici ce que dit la norme (C99 6.3.2.1/3 - Autres opérandes - Valeurs L, tableaux et désignateurs de fonctions) :

Sauf lorsqu'il s'agit de l'opérande de l'opérateur sizeof ou de l'opérateur unaire &, ou encore d'un littéral de chaîne de caractères utilisé pour initialiser un tableau, une expression de type ''tableau de type'' est convertie en une expression de type ''pointeur de type'' qui pointe vers l'élément initial de l'objet tableau et qui n'est pas un linaire. l'objet tableau et n'est pas une lvalue.

Cela signifie qu'à peu près chaque fois que le nom du tableau est utilisé dans une expression, il est automatiquement converti en un pointeur vers le premier élément du tableau.

Notez que les noms de fonctions agissent de la même manière, mais les pointeurs de fonctions sont beaucoup moins utilisés et de manière beaucoup plus spécialisée que la conversion automatique des noms de tableaux en pointeurs, ce qui n'entraîne pas autant de confusion.

La norme C++ (4.2 Array-to-pointer conversion) assouplit l'exigence de conversion à (c'est moi qui souligne) :

Une valeur lval ou rval de type "array of N T" ou "array of unknown bound of T". puede être converti en une valeur r de type "pointeur vers T".

Donc la conversion n'est pas ont comme cela se passe presque toujours en C (cela permet aux fonctions de surcharger ou aux modèles de correspondre au type de tableau).

C'est également pour cette raison qu'en C, vous devriez éviter d'utiliser des paramètres de tableau dans les prototypes/définitions de fonction (à mon avis - je ne suis pas sûr qu'il y ait un accord général). Ils sont source de confusion et sont de toute façon une fiction - utilisez des paramètres pointeurs et la confusion ne disparaîtra peut-être pas complètement, mais au moins la déclaration du paramètre ne ment pas.

2 votes

Quel est un exemple de ligne de code où une "expression de type 'tableau de type'" est "une chaîne littérale utilisée pour initialiser un tableau" ?

4 votes

@Garrett char x[] = "Hello"; . Le tableau de 6 éléments "Hello" ne se désintègre pas ; au contraire x obtient la taille 6 et ses éléments sont initialisés à partir des éléments de "Hello" .

34voto

John Bode Points 33046

"Décroissance" se réfère à la conversion implicite d'une expression à partir d'un tableau de type pour un type de pointeur. Dans la plupart des contextes, lorsque le compilateur voit un tableau de l'expression, il convertit le type de l'expression "N-élément de tableau de T" à "pointeur de T" et définit la valeur de l'expression à l'adresse du premier élément du tableau. Les exceptions à cette règle lorsqu'un tableau est un opérande de l' sizeof ou & opérateurs, ou le tableau est un littéral de chaîne utilisé comme un initialiseur dans une déclaration.

Supposons le code suivant:

char a[80];
strcpy(a, "This is a test");

L'expression a est de type "80-élément de tableau de char" et l'expression "Ceci est un test" est de type "16-élément de tableau de char" (en C, en C++, les littéraux de chaîne sont des tableaux de const char). Cependant, dans l'appel d' strcpy(), ni l'expression est un opérande de sizeof ou &, de sorte que leurs types sont implicitement converti en pointeur de char", et de leurs valeurs à l'adresse du premier élément de chaque. Ce strcpy() reçoit ne sont pas des tableaux, mais des pointeurs, comme on le voit dans son prototype:

char *strcpy(char *dest, const char *src);

Ce n'est pas la même chose qu'un pointeur sur le tableau. Par exemple:

char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;

Les deux ptr_to_first_element et ptr_to_array ont la même valeur; l'adresse de base d'une. Cependant, ils sont de types différents et sont traités différemment, comme indiqué ci-dessous:

a[i] == ptr_to_first_element[i] == (*ptr_to_array)[i] != *ptr_to_array[i] != ptr_to_array[i]

Rappelez-vous que l'expression a[i] est interprété comme *(a+i) (qui ne fonctionne que si le type du tableau est converti en un type pointeur), de sorte que les deux a[i] et ptr_to_first_element[i] fonctionner de la même. L'expression (*ptr_to_array)[i] est interprété comme *(*a+i). Les expressions *ptr_to_array[i] et ptr_to_array[i] peut conduire à des avertissements du compilateur ou des erreurs en fonction du contexte; ils vont certainement faire quelque chose de mal si vous vous attendez à eux de les évaluer à l' a[i].

sizeof a == sizeof *ptr_to_array == 80

Encore une fois, lorsqu'un tableau est un opérande de sizeof, il n'est pas converti en un type pointeur.

sizeof *ptr_to_first_element == sizeof (char) == 1
sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size
                                                  is on your platform

ptr_to_first_element est un simple pointeur vers char.

2 votes

N'est-ce pas ? "This is a test" is of type "16-element array of char" a "15-element array of char" ? (longueur 14 + 1 pour \0 )

9voto

C'est quand le tableau pourrit et qu'on le pointe du doigt ;-)

En fait, c'est juste que si vous voulez passer un tableau quelque part, mais que le pointeur est passé à la place (parce que qui diable passerait le tableau entier pour vous), les gens disent que le pauvre tableau s'est dégradé en pointeur.

0 votes

Bien dit. Quel serait un tableau agréable qui ne se dégrade pas en pointeur ou qui est empêché de se dégrader ? Pouvez-vous citer un exemple en C ? Merci.

0 votes

@Unheilig, bien sûr, on peut emballer un tableau dans un struct et passer le struct.

0 votes

Je ne suis pas sûr de ce que vous entendez par "travailler". Il n'est pas permis d'accéder au-delà du tableau, bien que cela fonctionne comme prévu si vous vous attendez à ce qui doit réellement se produire. Ce comportement (bien que, encore une fois, officiellement non défini) est préservé.

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