Brandon Yates
Brandon Yates

Reputation: 2052

Why can I not do arithmetic on a cast of a void pointer?

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

This doesn't compile in C. I know the alternative. But why does this not work? What is the problem?

Upvotes: 4

Views: 355

Answers (3)

R Sahu
R Sahu

Reputation: 206667

That's because

(char*)ptr is not an lvalue.

Try this instead:

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

Update

A brief explanation of various value types can be found at cppreference.com. This talks about value types in C++ but the core ideas translate to C.

For the purpose of this discussion,

An lvalue is an expression that identifies a non-temporary object or a non-member function.

You can take the address of an lvalue and assign to a different value.

Example:

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

In contrast,

A prvalue ("pure" rvalue) is an expression that identifies a temporary object (or a subobject thereof) or is a value not associated with any object.

The literal 42 is an rvalue. You cannot do:

int* p = &42;
42 = 53;

In this line,

    char* p = (char*)ptr;

an lvalue (p) is created from (char*)ptr. Hence, it is possible to use:

    p += numBytes;

Upvotes: 4

Filip Roséen
Filip Roséen

Reputation: 63832

The problem

The problem is that (char*)ptr will not yield an lvalue, meaning that the value cannot be modified - one can see it as a temporary result of the cast, the cast will yield a rvalue of type char*.

It's semantically the same as if you'd have the below example, a cast yields a temporary value, such a value cannot be assigned a new value.

int x = 123;

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

Solution

In your question you have stated that you already know a workaround to this problem, but I'd like to explicitly write the solution to show how one can modify the value of ptr even when casts yields non-modifiable values.

  1. Take the address of your pointer to void,
  2. cast this address to a pointer to pointer to char,
  3. dereference that pointer, yielding a pointer to char,
  4. modify this yield lvalue as if the original void* was of type char*

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

( Note: When dereferencing a pointer you get an lvalue, otherwise it would be impossible to change the value of the pointed to value located at an address stored in a pointer. )

Upvotes: 5

CS Pei
CS Pei

Reputation: 11047

On the left side of an =, you need an lvalue. But (char*)ptr is not an lvalue.

Upvotes: 3

Related Questions