web-dev-qa-db-fra.com

Arithmétique de pointeur pour pointeur vide en C

Lorsqu'un pointeur sur un type particulier (par exemple, int, char, float, ..) est incrémenté, sa valeur est augmentée de la taille de ce type de données. Si un pointeur void pointant vers des données de taille x est incrémenté, comment peut-il pointer x octets en avant? Comment le compilateur sait-il comment ajouter x à la valeur du pointeur?

156
Siva Sankaran

Conclusion finale: l'arithmétique sur un void* Est illégale en C et en C++.

GCC le permet comme extension, voir Arithmétique sur void- et pointeurs de fonction (notez que cette section fait partie du chapitre "Extensions C" de ce manuel). Clang et ICC autorisent probablement l'arithmétique void* À des fins de compatibilité avec GCC. D'autres compilateurs (tels que MSVC) refusent l'arithmétique sur void*, Et GCC l'interdit si l'indicateur -pedantic-errors Est spécifié ou si l'indicateur -Werror-pointer-arith Est spécifié (cet indicateur est utile si votre base de code doit également compiler avec MSVC).

La norme C parle

Les citations sont extraites du brouillon n1256.

La description standard de l'opération d'addition dans la norme indique:

6.5.6-2: Pour plus d'addition, les deux opérandes doivent avoir un type arithmétique ou un opérande doit être un pointeur sur un type d'objet et l'autre doit être de type entier.

La question est donc de savoir si void* Est un pointeur sur un "type d'objet" ou, de manière équivalente, si void est un "type d'objet". La définition de "type d'objet" est:

6.2.5.1: Les types sont partitionnés en types d'objets (types décrivant en détail les objets), types fonctions (types décrivant les fonctions) et types incomplets (types qui décrivent des objets mais manquent d'informations nécessaires pour déterminer leur taille).

Et la norme définit void comme:

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

Puisque void est un type incomplet, ce n'est pas un type d'objet. Par conséquent, ce n'est pas 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 autorisée, à cause de ces sections de la norme C:

6.2.5-27: Un pointeur sur void 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 impliquent l'interchangeabilité en tant qu'arguments des fonctions, renvoient des valeurs depuis des fonctions et des membres d'unions.

Donc, cela signifie que printf("%s", x) a la même signification si x a le type char* Ou void*, Mais cela ne signifie pas que vous pouvez faire de l'arithmétique sur une void*.

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

257
M. Sadeq H. E.

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

57
Job

transformez-le en pointeur de caractère et incrémentez votre pointeur d'avance x octets.

16
Ultimate Gobblement

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

14
Oliver Charlesworth

Le norme C ne permet pas void arithmétique de pointeur. Cependant, GNU C est autorisé en considérant 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'objet incomplet qui ne peut pas être complété.

Le programme suivant fonctionne correctement dans le compilateur GCC.

#include<stdio.h>

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.

11
msc

Vous devez le transtyper vers un autre type de pointeur avant de faire de l'arithmétique de pointeur.

8
Jakob

Les pointeurs vides peuvent pointer sur n’importe quel morceau de mémoire. Par conséquent, le compilateur ne sait pas combien d'octets à incrémenter/décrémenter lorsque nous essayons de calculer l'arithmétique de pointeur sur un pointeur vide. Par conséquent, les pointeurs vides doivent d'abord être convertis en un type connu avant de pouvoir être impliqués dans une arithmétique de pointeur.

void *p = malloc(sizeof(char)*10);
p++; //compiler does how many where to pint the pointer after this increment operation

char * c = (char *)p;
c++;  // compiler will increment the c by 1, since size of char is 1 byte.
5
user1581106

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

  • x+1 Ajoute un octet à x, le pointeur passe à 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 s'oppose au Galateo de C/C++, il est néanmoins correct en langage C, ce qui signifie qu'il compilera quelque chose sur la plupart des compilateurs nécessitant éventuellement un drapeau approprié (comme -Wpointer-arith).

0
rytis