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 <stdio.h>
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.
&arr
donne un tableau pointeur , un type de pointeur spécial int(*)[5]
qui pointe le tableau dans son ensemble.arr
, lorsqu'elle est écrite dans une expression telle que int *q = arr;
, "se décompose" en pointeur sur le premier élément. Complètement équivalent à int *q = &arr[0];
Dans le premier cas, vous essayez d’affecter une int(*)[5]
à un int*
. Ce sont des types de pointeurs incompatibles, d’où le message de diagnostic du compilateur.
Il s'avère que 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.
TL; DR Vérifiez les types.
&arr
est de 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, (l'emphase mienne)
Sauf s'il s'agit de l'opérande de l'opérateur
sizeof
, de l'opérateur_Alignof
ou du unaire&
, ou est un littéral utilisé pour initialiser un tableau, une expression qui a type ‘‘ ‘array of type’ ’ est converti en une expression de type ‘ ’pointeur sur type’ ’ qui pointe à l'élément initial de l'objet tableau et n'est pas une 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é le décalage de type, le La valeur est identique, donc cela semble fonctionner.
Voici les manières 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;
Lorsqu'il est utilisé dans une expression en tant que lvalue, un tableau décompose en un pointeur sur son premier élément. Donc, 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'objet. Ainsi, 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 int *p = arr;
correct. 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 le chien, etc.
La sortie est la même car 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 aux tableaux. Il existe d'innombrables tutoriels, dont certains montrent probablement vos deux cas à titre d'exemple.