Het casten van pointers is een veelvoorkomende operatie in de C-taal, waarmee je algemene interfaces kunt gebruiken, bijvoorbeeld door met geheugen te werken via void* of door universele datastructuren te implementeren. Het casten van pointer types gaat echter gepaard met bepaalde risico's en moet voldoen aan strikte standaarden.
void* in C slaat een adres op, maar weet niets van het type inhoud waarnaar het wijst. Elke andere pointer (bijvoorbeeld int*, char*, struct mytype*) kan expliciet (of impliciet) naar void* worden gecast en weer terug zonder informatieverlies (tenzij er "afkapping" plaatsvindt).void*), moet je er zeker van zijn dat op dat adres daadwerkelijk een waarde van een compatibel type staat.void process(void *data) { int *arr = (int*)data; // gebruik arr als een int array } int main() { double x = 10; process(&x); // GEVAARLIJK: cast double* naar int*, UB }
"Kan elke pointer veilig naar void* en terug worden gecast zonder verlies?"
Velen antwoorden "ja" — immers, de C-standaard garandeert het casten van elke object pointer naar void* en terug zonder verlies. Maar het is belangrijk te onthouden: als je een niet-object pointer (bijvoorbeeld een functie pointer) naar void* cast of verschillende architecturen mengt (de groottes van pointers verschillen voor functies en gegevens), krijg je onbepaald gedrag.
void foo() {} void *p = (void*)foo; // UB! functie pointer kan niet zo worden gecast
Verhaal
In een project met een cross-platform datasysteem werd een handler gebruikt die een pointer naar een struct naar
void*castte, en vervolgens weer terug naar het originele type. Bij de overstap naar een architectuur waarint*endouble*verschillende alignementen hadden, leidde de poging omvoid*naar een ongeldig type te casten tot een "bus error" (crash).
Verhaal
In een embedded project implementeerde een programmeur een ringbuffer met een universele interface op
void*, maar vergat de strikte vereisten voor alignment (geheugen toekennen voor eenchararray, doorsturen alsint*). Op sommige platformen werden de gegevens "onbegrijpelijk voor de hardware" — leesfouten en onbetrouwbaar gedrag ontstonden.
Verhaal
In een dynamische collectie gebruikten ze het opslaan van adressen als
void*, maar vergaten dat een functie pointer niet naarvoid*kan worden gecast. De poging om een event handler (callback) via dat veld op te slaan en door te geven leidde tot crashes op slechts enkele platforms, waarbij het zeer moeilijk was om de fout op te sporen.