Initialisation à partir d’un avertissement de type de pointeur incompatible lors de l’atsortingbution d’un pointeur

GCC me donne un avertissement ‘Initialisation à partir d’un type de pointeur incompatible’ lorsque j’utilise ce code (même si le code fonctionne bien et fait ce qu’il est censé faire, à savoir imprimer tous les éléments du tableau).

#include  int main(void) { int arr[5] = {3, 0, 3, 4, 1}; int *p = &arr; printf("%p\n%p\n\n", p); for (int a = 0; a < 5; a++) printf("%d ", *(p++)); printf("\n"); } 

Cependant, aucun avertissement n’est donné lorsque j’utilise ce bit de code

 int main(void) { int arr[5] = {3, 0, 3, 4, 1}; int *q = arr; printf("%p\n%p\n\n", q); for (int a = 0; a < 5; a++) printf("%d ", *(q++)); printf("\n"); } 

La seule différence entre ces deux extraits est que j’assigne * p = & arr et * q = arr.

  • Exactement quelle est la différence?
  • Et pourquoi le code s’exécute-t-il et donne-t-il exactement la même sortie dans les deux cas?

  • &arr donne un pointeur de tableau , un type de pointeur spécial int(*)[5] qui pointe sur le tableau dans son ensemble.
  • arr , lorsqu’il est écrit dans une expression telle que int *q = arr; , “se désintègre” en un pointeur sur le premier élément. Complètement équivalent à int *q = &arr[0];

Dans le premier cas, vous essayez d’affecter un int(*)[5] à un int* . Ce sont des types de pointeurs incompatibles, d’où le message de diagnostic du compilateur.

En fait, le pointeur de tableau et le pointeur int du premier élément auront très probablement la même représentation et la même adresse en interne. C’est pourquoi le premier exemple “fonctionne” même s’il n’est pas correct C.

Voici les moyens de pointer sur un (début de) tableau (sans avertissement), les deux fonctionnent:

 int *q = arr; /* OR */ int *q = &arr[0]; 

Celui-ci est quelque chose entre les deux, et générera un avertissement:

 int *q = &arr; 

La sortie est la même parce que l’adresse de arr[0] est littéralement équivalente au pointeur sur arr[] . Tout pointeur initialisé pour pointer sur arr[0] aura pour valeur l’adresse de arr[0] ; c’est ce qu’est un pointeur. Lisez sur le pointeur et en particulier sur leur relation avec les tableaux. Il existe d’innombrables tutoriels, dont certains montrent probablement vos deux cas à titre d’exemple.

TL; DR Vérifiez les types.

  • &arr est du type int (*) [5] ( pointeur sur un tableau de 5 int s ).
  • arr est de type int [5] , mais pas toujours .

Citant C11 , chapitre §6.3.2.1, (c’est moi qui souligne )

Sauf s’il s’agit de l’opérande de l’opérateur sizeof , de l’opérateur _Alignof ou de l’opérateur unaire & , ou d’un littéral de chaîne utilisé pour initialiser un tableau, une expression de type ” tableau de type ” est convertie en expression avec type ” pointeur sur type ” qui pointe sur l’élément initial de l’object tableau et n’est pas une valeur lvalue.

Par conséquent,

  int *q = arr; // int[5] decays to int *, == LHS 

et

  int *q = &arr[0]; // RHS == LHS 

sont les mêmes, alors que

  int *q = &arr; // LHS (int *) != RHS (int (*) [5]) 

est une expression de type incompatible.

Maintenant, cela fonctionne car, comme déjà mentionné dans la réponse de Lundin , l’adresse de la variable de tableau sera probablement la même que l’adresse du premier élément du tableau. Ainsi, malgré la disparité des types, la valeur est identique. semble fonctionner.

Lorsqu’il est utilisé dans une expression en tant que lvalue, un tableau se décompose en un pointeur sur son premier élément. So int *q = arr; initialise le pointeur int q avec l’adresse du premier élément du tableau arr : tout va bien et aucun avertissement n’est émis.

Mais &arr est l’adresse d’un tableau. Il ne peut être utilisé correctement que pour initialiser (ou assigner à) un pointeur sur un tableau ou une taille identique, ou un pointeur sur un tableau de taille indéterminée. Vous l’utilisez pour initialiser un pointeur sur int (qui est un type différent et non compatible) et le compilateur vous en avertit. Parce que l’utilisation d’un pointeur initialisé à partir d’un pointeur vers un type différent est un comportement indéfini selon la norme.

Mais sur les implémentations communes, les pointeurs sur n’importe quel type ont la même représentation qui est l’adresse du premier octet de l’object. Donc, même si cela n’est pas autorisé par la norme, l’instruction int *p = arr; se termine avec la même valeur pour p que donnerait le bon int *p = arr; . Cela explique pourquoi votre programme donne toujours la valeur attendue.

BTW, Undefined Behavior n’interdit pas les résultats escomptés, simplement un compilateur différent pourrait donner des résultats différents, planter, s’arrête prématurément sans erreur, botter votre chien, etc.