Quel est le problème avec la diffusion comme (void **) & device_array?

Il existe cette réponse à une autre question sur l’utilisation de cudaMalloc((void**)&device_array, num_bytes) , qui utilise void** comme argument de sortie au lieu de passer un void* comme valeur de retour, comme le malloc standard.

Il critique l’API de NVIDIA et déclare:

La conversion, comme dans (void **) & device_array, est un C invalide et entraîne un comportement indéfini.

et a été voté à plusieurs resockets (8 à partir de maintenant), donc je suppose qu’il y a une part de vérité.

Je ne comprends pas ce qui ne va pas avec le casting là-bas.

  • Qu’est-ce qui est invalide C ici?
  • Dans quel cas cela conduirait-il à un comportement indéfini?

Tout ce que je sais, c’est qu’il comstack sans avertissement et fonctionne avec le comportement prévu pour moi. Mais je ne connais pas C jusqu’au niveau de spécification standard.

Le problème est que void* a une signification particulière en C, avec des règles spéciales (1). C’est le seul type de pointeur vers / à partir duquel vous pouvez convertir en toute sécurité tout autre type de pointeur. Cependant, ces règles spéciales ne s’appliquent pas de manière récursive à void** .

Ce qui signifie que code comme int* ptr = malloc(x); est parfaitement bien, mais

 int* ptr; cudaMalloc(&ptr, x); // bad 

n’est pas bien! Une conversion de pointeur d’ int** à void** n’est pas bien définie. En théorie, cela pourrait entraîner un comportement indéfini et un désalignement (2).

De plus, il pourrait aussi y avoir des problèmes avec le crénelage du pointeur. Le compilateur est libre de supposer que le contenu d’un void** n’est jamais accessible via un int** et peut donc optimiser le code de manière inattendue, ce qui entraîne un comportement indéfini en cas de violation de la règle de crénelage ssortingct (6.5).

Ce qui signifie que vous devrez écrire un code comme celui-ci pour pouvoir utiliser la fonction en toute sécurité:

 void* vptr; int* iptr; cudaMalloc(&vptr, x); iptr = vptr; 

(1) C11 6.3.2.3/1:

Un pointeur à annuler peut être converti en ou à partir d’un pointeur en n’importe quel type d’object. Un pointeur sur n’importe quel type d’object peut être converti en un pointeur à annuler et inverser; le résultat doit être égal au pointeur d’origine.

(2) C11 6.3.2.3/7:

Un pointeur sur un type d’object peut être converti en un pointeur sur un type d’object différent. Si le pointeur résultant n’est pas correctement aligné pour le type référencé, le comportement est indéfini.