2010-10-25 21 views
12

est valide cettePuis-je faire de l'arithmétique sur des pointeurs void * en C?

void *p = &X; /* some thing */ 
p += 12; 

et si oui, qu'est-ce que maintenant p pointer? J'ai un code (tiers) qui fait cela (et compile proprement) et je suppose que le void * a été traité comme un char *. Mon fidèle K & R est silencieux (ish) sur le sujet

EDIT: Ma petite application de test tourne bien sur gcc 4.1.1 et traite void * comme char *. Mais g ++ barfs

Je sais comment le faire correctement. J'ai besoin de savoir si je dois nettoyer cette base de code pour trouver tous les endroits qu'il a fait.

gcc BTW -pedantic jette un avertissement

Résumé:

La spécification C est ambiguë. Il dit cela en termes de représentation et d'utilisation comme paramètres de fonction void * = char *. Mais il est silencieux en ce qui concerne l'arithmétique du pointeur.

  • gcc (4) permet et traite comme char *
  • g ++ refuse
  • -pedantic gcc avertit à ce sujet
  • VS2010 à la fois C et C++ refuse
+1

liés? http://stackoverflow.com/questions/1997751/c-gcc-compiler-options-for-pointer-arithmetic-warning – pmg

Répondre

4

Cela dépend du compilateur. Ceux qui le permettent considèrent sizeof (* (void *)) comme 1.

EDIT: c'est seulement pour l'arithmétique du pointeur de vide. Cela n'aurait aucun sens d'utiliser dans ce cas les étapes de sizeof (int) ou de 0. Les attentes communes de quelqu'un qui l'utilise seraient les plus petites étapes possibles.

+1

Quel compilateur permet cela? Cela implique que 'sizeof (void) == 1' qui n'est pas sain d'esprit. – JaredPar

+23

Peut-être pour de petites valeurs de 1. –

+4

@Jason, merci de perdre quelques minutes de ma journée alors que je me suis assis dans ma chaise en riant sans espoir :) – JaredPar

14

Non, ce n'est pas légal. Un void* ne peut pas être arbitrairement incrémenté. Il doit d'abord être casté à un type spécifique.

Si vous souhaitez l'incrémenter d'un nombre spécifique d'octets, c'est la solution que j'utilise.

p = ((char*)p) + 12; 

Le type char est pratique car il a une taille définie de 1 octet.

EDIT

Il est intéressant qu'il fonctionne sur gcc avec un avertissement. J'ai testé sur Visual Studio 2010 et vérifié qu'il ne compile pas. Ma compréhension limitée de la norme dirait que gcc dans l'erreur ici. Pouvez-vous ajouter les indicateurs de compilation suivants?

-Wall -ansi -pedantic 
+1

Visual Studio compile le code en tant que C++ par défaut, ce qui explique pourquoi il génère une erreur. – casablanca

+0

-pedantic lève un avertissement. compiler en tant que C++ fatals – pm100

+0

@casablanca Je suis toujours mystifié sur la façon dont incrémenter 'void *' pourrait être légal en C du tout. Cela ne semble certainement pas portable. – JaredPar

1

Je ne pense pas que vous pouvez, car il ne connaît pas son type, ne peut donc pas chercher la bonne quantité d'octets.

Transformez-le en un type en premier, c'est-à-dire (int).

+0

Si vous lancez 'int' alors que le' + = 12' est essentiellement interprété comme '+ = sizeof (int) * 12' termes de combien d'octets ont été réellement ajoutés. Cela ne semble pas être le comportement voulu. – JaredPar

+0

@JaredPar Cela fonctionnerait-il si ce pointeur indiquait un entier? – alex

+0

dépend à nouveau de l'intention. Si l'intention était de déplacer 12 éléments vers l'avant, alors oui. Si l'intention était de le déplacer de 12 octets vers l'avant alors non. Difficile de lire l'intention de la question, mais mon hypothèse est que l'OP ne veut pas d'incrément de 12 octets. – JaredPar

2

Votre estimation est correcte.

Dans la norme ISO C99, section 6.2.5 paragraphe 26, il déclare que les pointeurs de caractère vide et les pointeurs de caractères auront la même représentation et les mêmes exigences d'alignement (paraphrase).

+2

On dit aussi au §6.5.6 paragraphe 2 que les deux opérandes ont des types arithmétiques ou un opérande a un type entier et l'autre opérande est un pointeur vers un type d'objet (un pointeur vers void n'est pas un pointeur vers un type d'objet). Donc, l'arithmétique sur un pointeur sur void n'est * pas * définie. – dreamlax

+0

@dreamlax: Et ** non défini ** signifie que le compilateur peut faire tout ce qu'il veut, y compris utiliser une taille d'élément de '1'. –

+0

@dreamlax Oui, d'accord. J'ai mal compris la note de bas de page 39. Au paragraphe 1 de l'article 6.3.2.3, il est dit que void * peut être converti de/vers n'importe quoi en toute impunité. Étant donné qu'il est nécessaire d'avoir les mêmes caractéristiques que char *, il semble logique qu'il soit implicitement converti en char * par le compilateur. Cela dit, je pense que c'est * vraiment * une mauvaise pratique de manipuler le vide * de cette façon. –

12

Pour citer la spécification:

§6.5.6/2: Pour plus, soit les deux opérandes doivent avoir le type arithmétique, ou un opérateur doit être un pointeur vers un type d'objet et l'autre doit avoir un type entier. (Incrémentation équivaut à ajouter 1.)

Un pointeur à vide est pas un pointeur sur un type d'objet, conformément à ces extraits:

§6.2.5/1: [. ..] Les types sont partitionnés en types d'objets (types qui décrivent complètement les objets), types de fonctions (types qui décrivent les fonctions) et types incomplets (types qui décrivent les objets mais manquent d'informations pour déterminer leurs tailles).

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

Par conséquent, l'arithmétique de pointeur est pas défini pour pointeur sur void types.

+0

+1 pour souligner que la spécification C n'est pas ambigu sur ce point. J'ajouterai que depuis 6.5.6/2 fait partie d'une section "Constraints", ce comportement n'est pas indéfini, mais un compilateur conforme est requis pour émettre un diagnostic (5.1.1.3/1). Dans le cas de 'gcc -pedantic', seulement un avertissement, mais néanmoins un diagnostic. –