Arithmétique du pointeur pour le pointeur vide dans C

Lorsqu’un pointeur vers un type particulier (say int , char , float , ..) est incrémenté, sa valeur est augmentée de la taille de ce type de données. Si un pointeur void qui pointe vers des données de taille x est incrémenté, comment parvient-il à pointer x octets avant? Comment le compilateur sait-il append x à la valeur du pointeur?

    Conclusion finale: l’arithmétique sur un void* est illégale à la fois en C et en C ++.

    GCC le permet en tant qu’extension, voir Arithmétique sur les pointeurs void et fonctionnels (notez que cette section fait partie du chapitre “Extensions C” du manuel). Clang et ICC autorisent probablement l’arithmétique void* à des fins de compatibilité avec GCC. D’autres compilateurs (tels que MSVC) interdisent l’arithmétique sur void* , et GCC ne le permet pas si l’ -pedantic-errors est spécifié, ou si l’ -Werror-pointer-arith est spécifiée (cet indicateur est utile si votre base de code doit également comstackr avec MSVC).

    Le standard C parle

    Les citations sont tirées du brouillon n1256.

    La description standard de l’opération d’ajout indique:

    6.5.6-2: De plus, soit les deux opérandes auront un type arithmétique, soit un opérande sera un pointeur sur un type d’object et l’autre aura un type entier.

    Donc, la question ici est de savoir si void* est un pointeur sur un “type d’object”, ou de manière équivalente, si void est un “type d’object”. La définition de “type d’object” est la suivante:

    6.2.5.1: Les types sont partitionnés en types d’object (types décrivant complètement les objects), types de fonction (types décrivant des fonctions) et types incomplets (types décrivant des objects mais dépourvus d’informations nécessaires pour déterminer leur taille).

    Et la norme définit le void comme void :

    6.2.5-19: Le type void comprend un ensemble vide de valeurs; c’est un type incomplet qui ne peut être complété.

    Puisque void est un type incomplet, ce n’est pas un type d’object. Par conséquent, il ne s’agit pas d’un opérande valide pour une opération d’addition.

    Par conséquent, vous ne pouvez pas effectuer d’arithmétique de pointeur sur un pointeur void .

    Remarques

    À l’origine, on pensait que l’arithmétique void* était permise, à cause de ces sections de la norme C:

    6.2.5-27: Un pointeur à annuler doit avoir les mêmes exigences de représentation et d’alignement qu’un pointeur sur un type de caractère.

    cependant,

    Les mêmes exigences de représentation et d’alignement sont censées impliquer l’interchangeabilité en tant qu’arguments de fonctions, valeurs de retour de fonctions et membres d’unions.

    Donc, cela signifie que printf("%s", x) a la même signification, que x ait le type char* ou void* , mais cela ne signifie pas que vous pouvez faire de l’arithmétique sur un void* .

    Note de l’éditeur: Cette réponse a été modifiée pour refléter la conclusion finale.

    L’arithmétique de pointeur n’est pas autorisée sur les pointeurs void* .

    le convertir en un pointeur de caractère incrémente votre pointeur vers l’avant de x octets.

    Vous ne pouvez pas faire de l’arithmétique de pointeur sur les types void * , exactement pour cette raison!

    Vous devez le convertir en un autre type de pointeur avant de faire l’arithmétique du pointeur.

    Les pointeurs vides peuvent pointer vers n’importe quel morceau de mémoire. Par conséquent, le compilateur ne sait pas combien d’octets incrémenter / décrémenter lorsqu’on tente une arithmétique de pointeur sur un pointeur vide. Par conséquent, les pointeurs de vide doivent être placés en premier sur un type connu avant de pouvoir être impliqués dans une arithmétique de pointeur.

     void *p = malloc(sizeof(char)*10); p++; //comstackr does how many where to pint the pointer after this increment operation char * c = (char *)p; c++; // comstackr will increment the c by 1, since size of char is 1 byte. 

    Le standard C n’autorise pas l’arithmétique du pointeur vide . Cependant, GNU C est autorisé en considérant que la taille de void est 1 .

    Norme C11 §6.2.5

    Paragraphe 19

    Le type void comprend un ensemble vide de valeurs; c’est un type d’object incomplet qui ne peut pas être complété.

    Le programme suivant fonctionne bien dans le compilateur GCC.

     #include int main() { int arr[2] = {1, 2}; void *ptr = &arr; ptr = ptr + sizeof(int); printf("%d\n", *(int *)ptr); return 0; } 

    Peut-être que d’autres compilateurs génèrent une erreur.

    Le compilateur sait par type cast. Étant donné un void *x :

    • x+1 ajoute un octet à x , le pointeur passe à l’octet x+1
    • (int*)x+1 ajoute sizeof(int) octets, le pointeur passe à l’octet x + sizeof(int)
    • (float*)x+1 addres sizeof(float) octets, etc.

    Bien que le premier élément ne soit pas portable et soit contre le Galateo de C / C ++, il est néanmoins correct en langage C, ce qui signifie qu’il comstackra quelque chose sur la plupart des compilateurs nécessitant éventuellement un drapeau approprié (comme -Wpointer-arith)