2016-03-16 14 views
17

La sottrazione di indirizzi di puntatori non divisibili è definita in C? In C++?Sottrazione di indirizzi di puntatore non divisibili

Ecco un esempio:

void* p = malloc(64); 

int* one = (int*)((char*)p); 
int* two = (int*)((char*)p + 7); 

printf("%x %x %d %d\n", one, two, sizeof(int), two - one); 

Ideone link.

Ottengo l'output 8a94008 8a9400f 4 1, quindi sembra che esegua la divisione e tronca il resto. Il comportamento è definito?

+8

È stato invocato * comportamento non definito * passando dati con tipo errato a 'printf()'. L'affermazione corretta da stampare sarà 'printf ("% p% p% zu% td \ n ", (void *) uno, (void *) two, sizeof (int), two-one);' – MikeCAT

+1

I do not avere una citazione L'aritmetica del puntatore si basa sull'aritmetica intera e ciò è ben definito. Quindi penso che "due - uno" sia ben definito. anche se i puntatori non sono imballati come una persona ragionevole si aspetterebbe. D'altra parte - non farlo, nessuno lo vedrà nella tua base di codice. – Johannes

+3

Anche solo tenere quel puntatore 'two' nel tuo programma non è valido in quanto il puntatore non soddisfa i vincoli di allineamento per' int' (anche se in pratica su gran parte dell'hardware non innescherà un errore fino a quando non proverai a dereferenziarlo su un processore che non corregge l'accesso alla memoria non allineato come ARM) – Thomas

risposta

20

Questo comportamento non è definito secondo 5.7.6:

Quando due puntatori a elementi dello stesso oggetto array vengono sottratti, il risultato è la differenza tra gli indici dei due elementi dell'array. [...] A meno che entrambi i puntatori non puntino a elementi dello stesso oggetto di matrice o uno sull'ultimo elemento dell'array, il comportamento non è definito.

Nel codice, puntatore two non è orientata ad un elemento dello stesso int array come puntatore one. Infatti, non punta a nessun elemento dell'array di p, perché punta al "centro" di uno degli elementi (che di per sé è un comportamento non definito).

+4

Questo è vero, ma UB succede la prima volta in '(int *) ((char *) p + 7)'. – user694733

+0

@ user694733 Sei sicuro che un cast non valido causi UB anche senza un dereferenziamento? Stavo cercando di trovare qualcosa in questo senso nello standard, ma non ho trovato una risposta definitiva. – dasblinkenlight

+0

È possibile convertire il puntatore da malloc in qualsiasi tipo di puntatore incorporato. Tuttavia, per '(char *) p + 7' non è più valido, la conversione da' char * 'a' int * 'non è valida. – user694733

19

In alcune ipotesi , in C terza riga:

int* two = (int*)((char*)p + 7); 

provoca già comportamento indefinito, perché il puntatore p non è allineato correttamente per il tipo si fa riferimento .


L'ipotesi è che i requisiti di allineamento per tipo int sono essere superiore per il tipo char. Questo è vero per la maggior parte delle architetture moderne. Dal momento che tutti gli allineamenti devono essere potenze di due e il valore 7 non lo è, l'aggiunta di quel valore al puntatore p non può produrre un puntatore con un allineamento tanto rigoroso quanto il requisito di allineamento per il tipo int.

(Citato da: ISO/IEC 9899: 201x 6.3.2.3 puntatori 7.)
Un puntatore a un tipo di oggetto può essere convertito in un puntatore a un tipo di oggetto diverso. Se il puntatore risultante non è allineato correttamente per il tipo di riferimento, il comportamento è indefinito.

(Citato da: ISO/IEC 9899: 201x 6.2.8 Allineamento di oggetti 4.)
Ogni valore di allineamento valido deve essere una potenza integrale non negativa di due.

+1

Al momento non riesco a trovare il capitolo e il verso, ma lo stesso vale per il C++. Il calcolo di 'two' sarà un comportamento indefinito. (Immaginate una macchina basata su parole con registri speciali per i puntatori. Il calcolo di 'due' passerà attraverso uno di questi registri, e potrebbe sfuggire al puntatore disallineato - tali macchine sono esistite in passato.) –