Est-il légal de modifier un pointeur de données à travers un vide **

Est-il légal d’accéder à un type de pointeur via un void ** ?

J’ai parcouru les citations sur les normes relatives au crénelage de pointeur mais je ne sais toujours pas si c’est légal ou non:

 int *array; void **vp = (void**)&array; *vp = malloc(sizeof(int)*10); 

Exemple sortingvial, mais cela s’applique à une situation plus complexe que je vois.

Il semble que ce ne serait pas légal puisque j’accède à un int * via une variable dont le type n’est ni int * ni char * . Je ne peux pas en arriver à une conclusion simple à ce sujet.

En relation:

  • Est-ce que C a un type générique “pointeur sur un pointeur”?
  • C-FAQ question 4.9

Un pointeur sur différents types peut avoir différentes tailles.

Vous pouvez stocker un pointeur de n’importe quel type dans un void * et le récupérer, mais cela signifie simplement qu’un void * doit être suffisamment grand pour contenir tous les autres pointeurs.

Traiter une variable qui détient un int * comme s’il s’agissait d’un void * n’est en général pas autorisé.

Notez également que faire un casting (par exemple, caster vers int * le résultat de malloc ) est complètement différent de traiter une zone de mémoire contenant un int * comme il contient un void * . Dans le premier cas, le compilateur est informé de la conversion si nécessaire, dans le second, vous fournissez de fausses informations au compilateur.

Sur X86, toutefois, elles ont normalement la même taille et vous êtes en sécurité si vous jouez uniquement avec des pointeurs vers des données (les pointeurs vers des fonctions peuvent toutefois être différents).

À propos du crénelage de toute opération d’écriture effectuée via un void * ou un char * possible de muter n’importe quel object. Le compilateur doit donc envisager le crénelage autant que possible. Ici cependant, dans votre exemple, vous écrivez à travers un void ** (une chose différente) et le compilateur est libre d’ignorer les effets de crénelage potentiellement int * .

void ** a un type spécifique (pointeur sur un pointeur sur un vide). Par exemple, le type sous-jacent du pointeur est “pointeur sur vide”

Vous ne stockez pas une valeur de pointeur similaire lorsque vous stockez un pointeur sur int. Le fait qu’une dissortingbution soit requirejse est un indicateur fort que ce que vous faites n’est pas un comportement défini par la norme (et ce n’est pas le cas). Chose intéressante, cependant, vous pouvez utiliser un void* régulier void* qui va et vient et qui présentera un comportement défini. En d’autres termes, ceci:

 #include  #include  int main() { int *array; void *vp = &array; int **parray = vp; *parray = malloc(sizeof(int)*10); } 

est légitime. Votre exemple original ne sera même pas compilé si je supprime la fonte et utilise apple llvm 4.2 (clang), précisément en raison de types de pointeurs incompatibles, c’est-à-dire du sujet même de votre question. L’erreur spécifique est:

“Les types de pointeurs incompatibles initialisant ‘void **’ avec une expression de type ‘int **'”

et à juste titre.

Votre code peut fonctionner sur certaines plates-formes, mais il n’est pas portable. La raison en est que C n’a pas de pointeur générique vers le type de pointeur. Dans le cas de void * la norme autorise explicitement les conversions entre lui et d’autres pointeurs en types complets / incomplets, mais ce n’est pas le cas avec void ** . Cela signifie que dans votre code, le compilateur n’a aucun moyen de savoir si la valeur de *vp été converti à partir d’un type autre que void * et ne peut donc pas effectuer de conversions à l’exception de celle que vous avez explicitement convertie.

Considérons ce code:

 void dont_do_this(struct a_t **a, struct b_t **b) { void **x = (void **) a; *x = *b; } 

Le compilateur ne se plaindra pas de la b_t * implicite de b_t * à void * dans la ligne *x = *b , même si cette ligne tente de placer un pointeur sur un b_t à un endroit où seuls les pointeurs vers a_t doivent être placés. L’erreur est en fait dans la ligne précédente, qui convertit “un pointeur en un endroit où les pointeurs vers a_t peuvent être placés” en “un” pointeur vers un endroit où les pointeurs peuvent être placés “. C’est la raison pour laquelle aucune conversion implicite n’est possible. Pour un exemple analogue avec des pointeurs sur des types arithmétiques, voir la FAQ de C.

Votre dissortingbution, même si elle ferme l’avertissement du compilateur, est dangereuse car tous les types de pointeurs ne peuvent pas avoir la même représentation / taille interne (par exemple, void ** et int * ). Pour que votre code fonctionne dans tous les cas, vous devez utiliser un void * intermédiaire void * :

 int *array; void *varray = array; void **vp = &varray; *vp = malloc(sizeof(int) * 10);