Array consecutivi
Sono curioso di una frase nello standard C18:
Due puntatori sono uguali se e solo se entrambi sono puntatori nulli, entrambi sono puntatori allo stesso oggetto (incluso un puntatore a un oggetto e un suboggetto all'inizio) o funzione, entrambi sono puntatori a uno dopo l'ultimo elemento dello stesso array oppure uno è un puntatore a uno dopo la fine di un oggetto array e l'altro è un puntatore all'inizio di un oggetto array diverso che segue immediatamente il primo oggetto array nello spazio degli indirizzi. § 6.5.9 6
Perché l'oggetto che segue l'array deve essere necessariamente un altro array? Non potrebbe semplicemente essere un oggetto dello stesso tipo del tipo di base dell'array (come un intimmediatamente successivo a un int[])?
Non c'è da stupirsi che ho provato questo codice:
#include <stdio.h>
struct test { int arr[10]; int i; };
int main() {
struct test t;
int *p, *q;
p = t.arr + 10;
q = &t.i;
if (p == q)
printf("Equal pointers.");
return 0;
}
E produce indicatori uguali. Questo comportamento non è affatto garantito, solo una coincidenza definita dall'implementazione?
Risposte
OP: Perché l'oggetto che segue l'array deve essere necessariamente un altro array?
Non è così. "... inizio di un array diverso ..." è una semplificazione. La prossima specifica è:
Ai fini di questi operatori, un puntatore a un oggetto che non è un elemento di un array si comporta come un puntatore al primo elemento di un array di lunghezza uno con il tipo dell'oggetto come tipo di elemento. C17dr § 6.5.9 7
OP: Non potrebbe semplicemente essere un oggetto il cui tipo era lo stesso del tipo di base dell'array (come un
intimmediatamente successivo a unint[])?
Sì.
In primo luogo, specificare l'array qui non esclude/proibisce un singolo oggetto. Un singolo oggetto in memoria è indistinguibile da un array di dimensione 1.
( Modifica : leggi questa risposta per una citazione dallo standard che lo afferma esplicitamente quando si fa riferimento ai puntatori)
In secondo luogo, lo standard cerca anche di chiarire l'affermazione che hai citato, con la seguente nota a piè di pagina che indica gli scenari in cui si applica la regola:
Due oggetti possono essere adiacenti in memoria perché sono elementi adiacenti di una matrice più grande o membri adiacenti di una struttura senza riempimento tra di loro oppure perché l'implementazione ha scelto di posizionarli così, anche se non sono correlati.
Mettendo tutto insieme, ciò che lo standard cerca di dire qui è che in generale due puntatori a oggetti dissimili non dovrebbero essere uguali. Tuttavia, poiché è legale puntare uno oltre un oggetto array in memoria, se in quella posizione si trova un oggetto (array) diverso, è ancora legale che un tale puntatore sia uguale a un puntatore all'oggetto adiacente. Ora, potrebbe esserci o meno un oggetto valido in questa posizione, a causa delle scelte di allineamento e del riempimento, ma se ce n'è uno, è accettabile che questi puntatori siano uguali.
Nel tuo esempio, se cambiassi l'array in un carattere, i puntatori sarebbero probabilmente disuguali perché il compilatore sceglierebbe di allineare l'int a 4 byte (sulla maggior parte delle piattaforme a 32 o 64 bit), introducendo così il padding. Questo comportamento è ancora legale secondo lo standard.
#include <stdio.h>
struct test { char arr[10]; int i; };
int main() {
struct test t;
int *p, *q;
p = (int*)(t.arr + 10);
q = &t.i;
if(p == q)
printf("Equal pointers.");
else
printf("Unequal pointers.");
return 0;
}