Il casting dei puntatori è un'operazione comune nel linguaggio C, che consente di utilizzare interfacce generiche, ad esempio, lavorare con la memoria tramite void* o implementare strutture dati universali. Tuttavia, il casting dei tipi di puntatori comporta determinati rischi e segue standard rigorosi.
void* in C memorizza un indirizzo, ma non conosce il tipo del contenuto a cui punta. Qualsiasi altro puntatore (ad esempio, int*, char*, struct mytype*) può essere esplicitamente (o implicitamente) convertito in void* e viceversa senza perdita di informazioni (se non avviene "riduzione").void*), è necessario assicurarsi che all’indirizzo ci sia effettivamente un valore di tipo compatibile.void process(void *data) { int *arr = (int*)data; // utilizziamo arr come array di int } int main() { double x = 10; process(&x); // PERICOLOSO: castiamo double* a int*, UB }
"Qualsiasi puntatore può essere convertito in modo sicuro in void* e viceversa senza perdite?"
Molti rispondono "sì" — poiché lo standard C garantisce la conversione di qualsiasi puntatore a oggetti in void* e viceversa senza perdite. Ma è importante ricordare: se converti un puntatore non a oggetto (ad esempio, un puntatore a funzione) in void* o mescoli diverse architetture (le dimensioni dei puntatori differiscono tra funzioni e dati), otterrai un comportamento indefinito.
void foo() {} void *p = (void*)foo; // UB! non puoi convertire un puntatore a funzione in questo modo
Storia
In un progetto con un sottosistema di elaborazione dati multipiattaforma, è stato utilizzato un gestore che convertiva un puntatore a struttura in
void*, poi di nuovo nel tipo originale. Quando si è passati a un'architettura in cuiint*edouble*avevano diversi allineamenti, il tentativo di convertirevoid*in un tipo errato ha portato a un "bus error" (terminazione anomala).
Storia
In un progetto embedded, un programmatore ha implementato un buffer circolare con un'interfaccia universale in
void*, ma ha dimenticato i rigorosi requisiti di allineamento (ha allocato memoria per un array dichar, passato comeint*). Su alcune piattaforme, i dati sono diventati "incomprensibili per l'hardware" — si sono verificati errori di lettura e un funzionamento instabile.
Storia
In una collezione dinamica, sono stati utilizzati indirizzi memorizzati come
void*, ma è stato dimenticato che un puntatore a funzione non può essere convertito invoid*. Tentare di memorizzare e passare un gestore di eventi (callback) attraverso un campo del genere ha portato a crash solo su alcune piattaforme, e catturare l'errore era estremamente difficile.