Dubbio su nome array come puntatore

di il
9 risposte

Dubbio su nome array come puntatore

Buongiorno a tutti,

sono uno studente di Informatica e mi sto addentrando nello studio del linguaggio C.
Ho un dubbio circa l'interpretazione del nome di un array inteso come puntatore al primo elemento dell'array.
Faccio subito un esempio molto molto banale ma per andare dritti al problema:

#include <stdio.h>

int main() {

    int* p;
    int A[3] = {1,2,3};
    p = A;

    printf("Indirizzo di p: %X\n", &p);
    printf("Valore di p: %X\n",  p);
    printf("Indirizzo di A: %X\n", &A);
    printf("Valore di A: %X\n", A);

}
OUTPUT:
Indirizzo di p: 61FF1C
Valore di p: 61FF10
Indirizzo di A: 61FF10
Valore di A: 61FF10

Ora, mi è assolutamente chiaro il fatto che scrivere A oppure &A[0] sia la stessa cosa, non mi è invece chiaro come mai &A e A abbiano lo stesso valore.
In altre parole, stando alla definizione, se un puntatore è una variabile che contiene un indirizzo, e il nome di un array è un puntatore al primo elemento dell'array, come mai l'indirizzo di A coincide con il suo valore? Non dovrebbe, come nel caso di p, darmi due indirizzi differenti, uno per identificare la posizione del puntatore in memoria e l'altro che identifica l'indirizzo dell'oggetto puntato?

Spero di essere stato chiaro e grazie in anticipo a chi ha tempo/voglia di rispondermi.

9 Risposte

  • Re: Dubbio su nome array come puntatore

    Ciao, ti consiglio di iniziare a dare un'occhiata qui, poi se qualcosa non ti è chiaro fammi sapere.
  • Re: Dubbio su nome array come puntatore

    darknight_03 ha scritto:


    se un puntatore è una variabile che contiene un indirizzo, e il nome di un array è un puntatore al primo elemento dell'array, ...
    Il problema sta in questa proposizione, che hai scritto benissimo, tanto che può essere tradotta in forma logica:

    if (A) && if (B)

    Ora, A è vero, e B è falso. Dunque: TRUE && FALSE == FALSE; ed è il motivo per cui hai trovato un paradosso.
    Il nome dell'array può essere pensato come un puntatore costante al primo elemento dell'array ma in realtà non è un puntatore, è un tipo di dato astratto che rappresenta l'indirizzo del primo elemento, pertanto:

    array == &array == &array[0];

    sono tutti sinonimi.
  • Re: Dubbio su nome array come puntatore

    Vi ringrazio per le risposte.

    Credo di aver capito, in sostanza il nome dell'array(A) coincide con l'indirizzo del primo elemento (&A[0]) e, allo stesso tempo, con l'indirizzo dell'array (&A).
    Tuttavia, sebbene siano sinonimi, ho notato che delle differenze tra le tre scritture ci sono e il seguente codice credo che lo dimostri:
    
    #include <stdio.h>
    
    int main()
    {
        int A[3] = {1,2,3};
        
        printf("%X\n",A);
        printf("%X\n",&A[0]);
        printf("%X\n",&A);
        printf("%X\n",A+1);
        printf("%X\n",&A[0]+1);
        printf("%X\n",&A+1);
        printf("%u\n",sizeof(A));
        printf("%u\n",sizeof(&A[0]));
        printf("%u\n",sizeof(&A));
    
        return 0;
    }
    
    OUTPUT:
    61FF14
    61FF14
    61FF14
    61FF18
    61FF18
    61FF20
    12
    4
    4

    Ciò mi porta a fare le seguenti riflessioni:
    1) A è esattamente equivalente a &A[0], ovvero A corrisponde all'indirizzo del primo elemento dell'array: infatti se scrivo A + 1 oppure &A[0] + 1, in entrambi i casi mi sposto di 4 bytes, ovvero mi sposto al successivo intero presente nell'array;
    2) A è equivalente a &A ma per una ragione diversa rispetto al caso precedente, e tra l'altro solo in parte: se A (e &A[0]) corrisponde all'indirizzo del primo elemento dell'array, &A corrisponde all'indirizzo dell'intero array: tale differenza la si nota se guardiamo il differente output che si ottiene quando scriviamo A + 1 (o &A[0] + 1), ottenendo 61FF18, rispetto a &A + 1, che restituisce invece 61FF20; questo perché scrivendo &A + 1 non ci spostiamo all'elemento successivo dell'array, ma ci spostiamo all'indirizzo immediatamente successivo all'ultima cella dell'array, cioè alla prima posizione libera che troviamo dopo l'array A, ovvero ci spostiamo di tanti byte quanti sono quelli occupati dall'intero array.
    3) utilizzando l'operatore sizeof, sorprendentemente, A non viene interpretato come indirizzo, ma come "l'array stesso", in quanto restituisce 12 bytes, ovvero la dimensione dell'intero array; nel caso invece di &A[0] e &A, questi vengono interpretati effettivamente come indirizzi, in quanto restituisce i 4 bytes che compongono un qualunque indirizzo di memoria sul mio pc.

    E' corretto il mio ragionamento?

    P.S. Buona Pasqua a tutti
  • Re: Dubbio su nome array come puntatore

    Ma perché sei sorpreso? L'array non è sempre equivalente al puntatore al primo elemento e i casi di &, sizeof e string literals sono regolati dalle norme. Che poi sarebbe un bel casino se sizeof non restituisse la lunghezza dell'array. Magari il professore ve lo spiegherà più avanti
  • Re: Dubbio su nome array come puntatore

    Sono sorpreso proprio per il motivo che dici tu, nel senso che non c'è un'equivalenza assoluta. Il fatto che sia tutto regolato da norme del linguaggio non lo metto minimamente in discussione, la mia confusione è nata semplicemente dall'aver interpretato alla lettera la corrispondenza nome array/puntatore, mentre in realtà non è esattamente così.
  • Re: Dubbio su nome array come puntatore

    L'operatore sizeof su un puntatore o un indirizzo restituisce 4 o 8 a seconda che tu abbia compilato a 32 o 64 bit. C'è un'eccezione quando lo usi sul nome dei vettori, dove restituisce la dimensione totale del vettore in byte e probabilmente questo influisce anche quando vai a sommargli 1, in cui aggiunge 1*sizeof(A).
    Quando scrivi
    A[0]
    ti stai riferendo all'intero in posizione 0, con tutte le regole che ne conseguono sul tipo int.

    Il nome dell'array non è un puntatore, ma viene implicitamente interpretato come tale in certe occasioni. È un comportamento strano che sembra sia dovuto alla retrocompatibilità col linguaggio B.
  • Re: Dubbio su nome array come puntatore

    darknight_03 ha scritto:


    se A (e &A[0]) corrisponde all'indirizzo del primo elemento dell'array, &A corrisponde all'indirizzo dell'intero array
    Come dicevo prima, l'array è un tipo di dato astratto, e ogni volta che si ha a che fare con dati astratti non ha molto senso cercare di capire cosa fa la macchina, ha senso invece guardare le regole del linguaggio. Per intenderci, un dato astratto (in questo caso) è un costrutto composto a livello del compilatore diversamente dai tipi fondamentali che sono forniti direttamente dalla macchina. Un array sarà composto da un indirizzo di memoria, dalla dimensione del tipo e dalla dimensione totale dell'array, ma le variabili che il compilatore adopera per comporre tale costrutto sono nascoste al programmatore che si interfaccia ad esso solo tramite le regole offerte dal linguaggio. Ad esempio, quando l'operatore sizeof riceve il nome di un array, esso lo distingue da altri tipi e lo tratta secondo tali regole.

    darknight_03 ha scritto:


    tale differenza la si nota se guardiamo il differente output che si ottiene quando scriviamo A + 1 (o &A[0] + 1), ottenendo 61FF18, rispetto a &A + 1, che restituisce invece 61FF20; questo perché scrivendo &A + 1 non ci spostiamo all'elemento successivo dell'array, ma ci spostiamo all'indirizzo immediatamente successivo all'ultima cella dell'array
    Fosse corretto avresti dovuto trovare 0x61FF1C anziché 0x61FF20. O mi sbaglio?
    Anche se la scrittura &A è sinonimo di &A[0] o di A stesso, non è previsto che si utilizzi e nelle espressioni algebriche potrebbe fornire un tanto temuto "undefined behavior".

    Comunque, per divertirsi un po':
    A[n] == *(A + n);
    &A[n] == &*(A + n) == A + n;
    ma poiché:
    A + n == n + A
    salta fuori un trucchetto diabolico:
    A[n] == n[A];
    &A[n] == &n[A];
    Buona Pasqua!
  • Re: Dubbio su nome array come puntatore

    Fosse corretto avresti dovuto trovare 0x61FF1C anziché 0x61FF20. O mi sbaglio?
    In realtà, se &A rappresenta l'indirizzo dell'array ed è 61FF14, sommandogli + 1 ottengo l'indirizzo della prima posizione libera dopo l'array, cioè mi sposto di 1*sizeof(A) bytes, quindi in questo caso mi sposto di 12, ottenendo 61FF20 (cioè ->61FF14->61FF15->61FF16->61FF17->61FF18->61FF19->61FF1A->61FF1B->61FF1C->61FF1D->61FF1E->61FF1F->61FF20). Se scrivo &A + 2, avanzo di 2*size(A) = 2 * 12 = 24 bytes rispetto alla posizione inziale 61FF14, ottenendo 61FF2C, e così via.
    L'output di questo codice sembra confermare questo ragionamento: [CODE] int main() { int A[3] = {1,2,3}; printf("%X\n",&A); printf("%X\n",&A+1); printf("%X\n", &A+2); printf("%X\n", &A+3); return 0; } OUTPUT:
    61FF14
    61FF20
    61FF2C
    61FF38

    Scrivendo invece questo codice ottengo, come immaginavo, un output differente:
    
    
    #include <stdio.h>
    
    int main() {
    
        int A[3] = {1,2,3};
        printf("%X\n", A);
        printf("%X\n", A+1);
        printf("%X\n", A+2);
        printf("%X\n", A+3);
    
        return 0;
    
    }
    
    OUTPUT:
    61FF14
    61FF18
    61FF1C
    61FF20

    Da notare come A+3 e &A + 1 rappresentino lo stesso indirizzo, perché con A+3 ci spostiamo di 3 elementi, trovando il primo indirizzo "libero", con &A+1 idem ma calcolato spostandosi "direttamente", quindi senza scorrere gli indirizzi di ciascun elemento.
    Per questo dicevo che A e &A, sebbene abbiano lo stesso valore, hanno in realtà due significati semantici distinti, il primo è l'indirizzo del primo elemento e il secondo l'indirizzo dell'intero array, perché con A + 1 ci spostiamo all'indirizzo del secondo elemento dell'array, con &A + 1 ci spostiamo di una distanza pari alla dimensione dell'array, per vedere l'indirizzo della prima posizione libera.

    Ad ogni modo, al di là di questo, grazie a tutti quelli che hanno risposto, ognuno ha messo in luce degli aspetti molto interessanti sulla questione.

    Le prossime volte che mi salteranno fuori altri dubbi amletici saprò dove chiedere
  • Re: Dubbio su nome array come puntatore

    Si è corretto, mi ero sbagliato io a calcolare l'indirizzo.
    Oggettivamente &A incrementa di sizeof(A) mentre A e &A[0] incrementano di sizeof(A[0]).
    &A +n == A +n*sizeof(A)/sizeof(A[0]);
Devi accedere o registrarti per scrivere nel forum
9 risposte