Pourquoi ne puis-je pas faire d’arithmétique sur un casting d’un pointeur vide?

void foo(void *ptr, int numBytes) { (char*)ptr += numBytes; } 

Cela ne comstack pas en C. Je connais l’alternative. Mais pourquoi ça ne marche pas? Quel est le problème?

Le problème

Le problème est que (char*)ptr ne donnera pas de valeur lvalue , ce qui signifie que la valeur ne peut pas être modifiée – on peut la voir comme un résultat temporaire de la conversion, la conversion générant une valeur de type char* .

Sémantiquement, c’est la même chose que si vous aviez l’exemple ci-dessous, une conversion donne une valeur temporaire, une nouvelle valeur ne peut pas être affectée à cette valeur.

 int x = 123; (float)x += 0.12f; /* (1), illegal */ /* ^-- sementically equivalent to `123.f += 0.12f` */ 

Solution

Dans votre question, vous avez indiqué que vous connaissiez déjà une solution de contournement à ce problème, mais je voudrais écrire explicitement la solution pour montrer comment modifier la valeur de ptr même lorsque les conversions génèrent des valeurs non modifiables.

  1. Prenez l’adresse de votre pointeur pour annuler ,
  2. jette cette adresse sur un pointeur vers pointeur sur char ,
  3. déréférencer ce pointeur, ce qui donne un pointeur sur char ,
  4. modifier ce rendement lvalue comme si le void* initial void* était de type char*

 *((char**)&ptr) += numbytes; // make `ptr` move forward `numbytes` 

( Remarque : lorsque vous déréférenciez un pointeur, vous obtenez une lvalue . Dans le cas contraire, il serait impossible de modifier la valeur de la valeur pointée située à une adresse stockée dans un pointeur.)

Sur le côté gauche d’un = , vous avez besoin d’une lvalue . Mais (char*)ptr n’est pas une valeur.

C’est parce que

(char*)ptr n’est pas une valeur.

Essayez ceci à la place:

 void foo(void *ptr, int numBytes) { char* p = (char*)ptr; p += numBytes; } 

Mettre à jour

Une brève explication des différents types de valeur est disponible sur cppreference.com . Cela parle de types de valeur en C ++ mais les idées de base se traduisent en C.

Aux fins de cette discussion,

Une lvalue est une expression qui identifie un object non temporaire ou une fonction non membre.

Vous pouvez prendre l’adresse d’une valeur et l’assigner à une valeur différente.

Exemple:

 int i; int* p = &i; i = 20; 

En revanche,

Une valeur prvalue (“pure” rvalue) est une expression qui identifie un object temporaire (ou un sous-object de celui-ci) ou est une valeur qui n’est associée à aucun object.

Le 42 littéral est une valeur. Tu ne peux pas faire:

 int* p = &42; 42 = 53; 

Dans cette ligne,

  char* p = (char*)ptr; 

une lvalue ( p ) est créée à partir de (char*)ptr . Par conséquent, il est possible d’utiliser:

  p += numBytes;