Ho pensato di averlo davvero capito e la rilettura dello standard (ISO 9899: 1990) conferma semplicemente la mia comprensione ovviamente sbagliata, quindi ora chiedo qui.Puntatore contro array in C, differenza non banale
Il programma va in crash seguenti:
#include <stdio.h>
#include <stddef.h>
typedef struct {
int array[3];
} type1_t;
typedef struct {
int *ptr;
} type2_t;
type1_t my_test = { {1, 2, 3} };
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
type1_t *type1_p = &my_test;
type2_t *type2_p = (type2_t *) &my_test;
printf("offsetof(type1_t, array) = %lu\n", offsetof(type1_t, array)); // 0
printf("my_test.array[0] = %d\n", my_test.array[0]);
printf("type1_p->array[0] = %d\n", type1_p->array[0]);
printf("type2_p->ptr[0] = %d\n", type2_p->ptr[0]); // this line crashes
return 0;
}
Confrontando le espressioni my_test.array[0]
e type2_p->ptr[0]
secondo la mia interpretazione dello standard:
6.3.2.1 Array indicizzazione di
"La definizione del operatore operator [] è che E1 [E2] è identico a (* ((E1) + (E2))). "
Applicando questo dà:
my_test.array[0]
(*((E1)+(E2)))
(*((my_test.array)+(0)))
(*(my_test.array+0))
(*(my_test.array))
(*my_test.array)
*my_test.array
type2_p->ptr[0]
*((E1)+(E2)))
(*((type2_p->ptr)+(0)))
(*(type2_p->ptr+0))
(*(type2_p->ptr))
(*type2_p->ptr)
*type2_p->ptr
type2_p->ptr
è di tipo "puntatore a int" e il valore è l'indirizzo iniziale my_test
. *type2_p->ptr
pertanto valuta un oggetto intero la cui memoria è allo stesso indirizzo di my_test
.
Ulteriore:
6.2.2.1 lvalue, array e designatori funzione
"Tranne quando è l'operando del operatore sizeof o unario & operatore, ... delle lvalue che ha tipo
array of type
viene convertito in un'espressione con tipopointer to type
che punta all'elemento iniziale dell'array e non è un lvalue. "
my_test.array
ha tipo "array di int" ed è come descritto sopra convertito in "puntatore a int" con l'indirizzo del primo elemento come valore. *my_test.array
pertanto valuta un oggetto intero la cui memoria si trova allo stesso indirizzo del primo elemento dell'array.
Infine
6.5.2.1 Struttura e sindacali specificatori
Un puntatore a un oggetto struttura, opportunamente convertito, punti alla sua membro iniziale ..., e viceversa. È possibile che all'interno di un oggetto struttura ci sia un pad senza nome, ma non al suo inizio , se necessario per ottenere l'allineamento appropriato .
Poiché il primo membro della type1_t
la matrice, l'indirizzo iniziale di questo e l'intero type1_t
oggetto è lo stesso come sopra descritto. La mia comprensione è stata quindi che *type2_p->ptr
valuta a un numero intero la cui archiviazione è nello stesso indirizzo del primo elemento nell'array e pertanto è identico a *my_test.array
.
Ma questo non può essere il caso, in quanto il programma si blocca costantemente su Solaris, cygwin e Linux con le versioni gcc 2.95.3, 3.4.4 e 4.3.2 , in modo che qualsiasi questione ambientale è completamente fuori questione.
Dove è il mio ragionamento sbagliato/cosa non capisco? Come posso dichiarare type2_t per rendere ptr point al primo membro dell'array?
È sicuramente un comportamento definito. L'indirizzo di ptr è lo stesso dell'indirizzo di my_array. my_array è in realtà un puntatore nella struttura, mentre ptr è semplicemente un puntatore intero all'interno di una struttura. – Vitali
"comportamento definito" non significa "succede qualcosa", significa "il qualcosa che accade è definito dallo standard". Digitare la punteggiatura è un comportamento indefinito. Se vuoi vedere qualcosa di sorprendente accadere quando scrivi un gioco di parole, avvia le ottimizzazioni di una tacca o due sul tuo compilatore. –