web-dev-qa-db-fra.com

Initialisation à partir d'un avertissement de type de pointeur incompatible lors de l'attribution 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 <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.

  • 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?
6
Nathu
  • &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.

11
Lundin

TL; DR Vérifiez les types.

  • &arr est de type int (*) [5] (pointeur sur un tableau de 5 ints).
  • 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.

1
Sourav Ghosh

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;
1
SHG

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.

0
Serge Ballesta

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.

0
TomServo