Ci sono casi d’uso che si basano sulla rappresentazione di due allocazioni differenti in C?

Ad esempio, considera il seguente frammento di codice C:

void *p = malloc(1); void *q = malloc(1); bool question = (uintptr_t) p == (uintptr_t) q; 

Mi aspetto che tutti si aspettino che la question sia sempre falsa. (Sorprendentemente, lo standard C11 non lo richiede, tuttavia. L’unica restrizione su uintptr_t è eg ((void *) ((uintptr_t) p)) == p . Vedere 7.20.1.4 dello standard C11 per i dettagli.)

La mia domanda è: esiste un caso d’uso realistico che si basi effettivamente sulla garanzia che la question sia falsa o, più in generale, che le rappresentazioni dei numeri interi di due allocazioni siano diverse?

La domanda è difettosa. Innanzitutto, considera questo codice (dove supponiamo che malloc non restituisca null):

 void *p = malloc(1); void *q = malloc(1); bool question = (uintptr_t) p == (uintptr_t) q; 

Per le specifiche dello standard C 2011 di uintptr_t , se lo facciamo:

 void *p1 = (void *) (uintptr_t) p; void *q1 = (void *) (uintptr_t) q; 

Quindi p1 deve confrontare uguale a p , e q1 deve confrontare uguale a q . E, secondo le specifiche dello standard dell’operatore == , p1 e q1 non devono essere paragonabili tra loro, poiché sono, rispettivamente, uguali a p e a q , e quindi sono puntatori a oggetti diversi.

Poiché (void *) (uintptr_t) p e (void *) (uintptr_t) q producono valori diversi, (uintptr_t) p e (uintptr_t) q devono essere valori diversi. (Questo perché la conversione a void * è una funzione matematica all’interno del modello C. Ogni volta che viene assegnato un valore specifico, all’interno delle regole di C, produce un risultato che è effettivamente lo stesso, all’interno delle regole di C.) Quindi (uintptr_t) p non è uguale a (uintptr_t) q .

Ora considera questo codice, dove cambiamo void * in char * :

 char *p = malloc(1); char *q = malloc(1); bool question = (uintptr_t) p == (uintptr_t) q; 

Le specifiche di uintptr_t riguardano le conversioni da char * . Ovviamente, questi puntatori a char * possono essere convertiti in void * . Tuttavia, non vedo che lo standard richiede che una conversione da char * a uintptr_t debba implicitamente inserire una conversione in void * o agire come se lo facesse. Quindi suppongo che questo sia tecnicamente indefinito dallo standard. Ma dubito che troverai qualsiasi implementazione C in cui questo differisce dalla versione void * , tranne una costruita specificamente per violare questa.

Ciononostante, in effetti ogni programmatore che usa uintptr_t aspetta che il risultato identifichi completamente il puntatore originale (contiene tutte le informazioni per riconvertirsi al puntatore originale), e quindi si aspetta che il risultato di uintptr_t differisca da quello di tutti i puntatori diversi.

Per quanto riguarda un esempio specifico, il framework Accelerate in macOS prevede il risultato di convertire diversi puntatori in uintptr_t per produrre risultati diversi, e sono sicuro che anche molte altre parti di macOS lo fanno.