La conversion de pointeurs est une opération courante en langage C, permettant d'utiliser des interfaces génériques, par exemple, de travailler avec la mémoire via void* ou de réaliser des structures de données universelles. Cependant, la conversion de types de pointeurs comporte certains risques et est soumise à des normes strictes.
void* en C stocke une adresse, mais ne connaît pas le type du contenu pointé. Tout autre pointeur (par exemple, int*, char*, struct mytype*) peut être explicitement (ou implicitement) converti en void* et vice versa sans perte d'information (à condition qu'il n'y ait pas de "rétrécissement").void*), vous devez vous assurer que l'adresse contient effectivement une valeur de type compatible.void process(void *data) { int *arr = (int*)data; // utilisons arr comme un tableau d'int } int main() { double x = 10; process(&x); // DANGEREUX : conversion de double* vers int*, UB }
"Un pointeur quelconque peut-il être converti en toute sécurité vers void* et vice versa sans perte ?"
Beaucoup répondent "oui" — car la norme C garantit la conversion de tout pointeur d'objet en void* et vice versa sans perte. Mais il est important de se rappeler : si vous convertissez un pointeur non objet (par exemple, un pointeur de fonction) en void* ou si vous mélangez différentes architectures (les tailles des pointeurs diffèrent pour les fonctions et les données), vous obtiendrez un comportement indéfini.
void foo() {} void *p = (void*)foo; // UB! un pointeur de fonction ne peut pas être converti comme ça
Histoire
Dans un projet avec un sous-système de traitement de données multiplateforme, un gestionnaire a été utilisé qui convertissait un pointeur sur une structure en
void*, puis retour vers le type d'origine. Lors du passage à une architecture oùint*etdouble*avaient des alignements différents, tenter de convertirvoid*au mauvais type a provoqué une "erreur de bus" (arrêt anormal).
Histoire
Dans un projet embarqué, un programmeur a réalisé un buffer circulaire avec une interface universelle en
void*, mais a oublié les exigences strictes en matière d'alignement (allouant la mémoire pour un tableau dechar, le transmettant commeint*). Sur certaines plateformes, les données sont devenues "incompréhensibles pour le matériel" — des erreurs de lecture et un fonctionnement instable se sont produites.
Histoire
Dans une collection dynamique, des adresses étaient stockées comme
void*, mais on a oublié qu'un pointeur de fonction ne peut pas être converti envoid*. Tenter de stocker et de transmettre un gestionnaire d'événements (callback) via ce champ a provoqué des pannes uniquement sur certaines plateformes, et il était extrêmement difficile de détecter l'erreur.