Initialisatie van incompatibele aanwijzertype waarschuwing bij toewijzing aan een aanwijzer

GCC geeft me de waarschuwing ‘Initialisatie van incompatibel aanwijzertype’ wanneer ik deze code gebruik (hoewel de code prima werkt en doet wat hij moet doen, namelijk alle elementen van de array afdrukken).

#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");
}

Er wordt echter geen waarschuwing gegeven wanneer ik dit stukje code gebruik

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");
}

Het enige verschil tussen deze twee fragmenten is dat ik *p = &arr en *q = arr toewijs.

  • Wat is precies het verschil met de & maken?
  • En waarom wordt de code uitgevoerd en in beide gevallen exact dezelfde uitvoer gegeven?

Antwoord 1, autoriteit 100%

  • &arrgeeft een array-pointer, een speciaal pointertype int(*)[5]dat naar de array als geheel wijst .
  • arr, wanneer geschreven in een uitdrukking zoals int *q = arr;, “vervalt” in een pointer naar het eerste element. Volledig gelijk aan int *q = &arr[0];

In het eerste geval probeer je een int(*)[5]toe te wijzen aan een int*. Dit zijn incompatibele aanwijzertypes, vandaar het diagnostische bericht van de compiler.

Het blijkt dat de array-pointer en de int-pointer naar het eerste element hoogstwaarschijnlijk dezelfde representatie en hetzelfde adres intern hebben. Dit is de reden waarom het eerste voorbeeld “werkt”, ook al is het niet correct C.


Antwoord 2, autoriteit 21%

TL;DRControleer de typen.

  • &arris van het type int (*) [5](verwijst naar een array van 5 ints).
  • arris van het type int [5], maar niet altijd.

Ik citeer C11, hoofdstuk §6.3.2.1, (nadruk van mij)

Behalve wanneer het de operand is van de operator sizeof, de operator _Alignofof de operator
unaire &operator, of is een letterlijke tekenreeks die wordt gebruikt om een array te initialiseren, een uitdrukking die
type ‘‘array of type’’wordt geconverteerd naar een uitdrukking met type ‘‘pointer to type’’die wijst
naar het initiële element van het array-object
en is geen lvalue.

vandaar,

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

en

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

zijn hetzelfde, terwijl,

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

is een niet-overeenkomende type-expressie.

Nu werkt het, omdat, zoals al vermeld in Lundins antwoord, het adres van de arrayvariabele waarschijnlijk hetzelfde zijn als het adres van het eerste element van de array, dus ondanks het niet-overeenkomende type is de waardehetzelfde, dus dit lijkt te werken.


Antwoord 3, Autoriteit 11%

Dit zijn de manieren om te wijzen op een (begin van) array (zonder waarschuwing), beide werk:

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

Deze is iets daartussen en zal een waarschuwing genereren:

int *q = &arr;

Antwoord 4, Autoriteit 5%

De uitvoer is hetzelfde omdat het adres van arr[0]letterlijk gelijk is aan de aanwijzer op arr[]. Elke aanwijzer geïnitialiseerd om te wijzen op arr[0]heeft als waarde het adres van arr[0]; Dat is wat een aanwijzer is. Lees op de aanwijzer en vooral op hun relatie tot arrays. De zijn talloze tutorials die er zijn, waarvan sommige waarschijnlijk uw twee gevallen als voorbeelden laten zien.


Antwoord 5

Bij gebruik in een uitdrukking als een LVALUE, een array vervalt naar een aanwijzer naar het eerste element. Dus int *q = arr;initialiseert intwijzer qmet het adres van het eerste element van de array arr: Alles is prima en geen waarschuwing is geëxemiteerd.

Maar &arris het adres van een array. Het kon alleen correct worden gebruikt om (of toe te kennen aan) een aanwijzer op array of dezelfde grootte of een aanwijzer naar een reeks onbepaalde maat. U gebruikt het om een ​​aanwijzer te initialiseren naar INT (die een ander en niet-compatibel type is) en de compiler waarschuwt u daarover. Omdat het gebruik van een aanwijzer is geïnitialiseerd vanuit een aanwijzer naar een ander type is undefined gedrag volgens de norm.

Maar bij veelvoorkomende implementaties hebben pointers naar elk type dezelfde representatie, namelijk het adres van de eerste byte van het object. Dus zelfs als het niet door de standaard is toegestaan, eindigt de instructie int *p = arr;met dezelfde waarde voor pals de juiste int *p = arr;. Dat verklaart waarom je programma nog steeds de verwachte waarde geeft.

BTW, ongedefinieerd gedrag verbiedt het verwachte resultaat niet, gewoon een andere compiler kan andere resultaten geven, crasht, stopt voortijdig zonder fouten, schop je hond, enz. (zelfs als geen enkele compiler mijn hond zou kunnen raken tot nu toe 😉 )

Other episodes