jaypee
jaypee

Reputation: 75

C - expression must be a modifiable lvalue

I'm confused why my compiler is throwing an error in the following condition:

void funcExample (void * p_Buf, uint16_t len) 
{
    uint16_t i;

    for (i = 0; i < len; i++) {
        otherFunc (((uint8_t *)p_Buf)++); //error = expression must be a modifiable lvalue
    }
}

but if I cast prior to pass to otherFunc, it's fine because no problem incrementing a non-void pointer:

void funcExample (void * p_Buf, uint16_t len) 
{
    uint16_t i;
    uint8_t * p_Buf_8bit;

    p_Buf_8bit = (uint8_t *) p_Buf;   

    for (i = 0; i < len; i++) {
        otherFunc (p_Buf_8bit++); 
    }
}

can't the void pointer be incremented once cast? am i missing something fundamental here?

Upvotes: 6

Views: 9689

Answers (4)

2501
2501

Reputation: 25752

Cast operators in c:

6.5.4. p5 Preceding an expression by a parenthesized type name converts the value of the expression to the named type. This construction is called a cast. 104) A cast that specifies no conversion has no effect on the type or value of an expression.

104) A cast does not yield an lvalue. Thus, a cast to a qualified type has the same effect as a cast to the unqualified version of the type

But the unary operator ++ requires an lvalue as stated:

6.5.3.1. p1 The operand of the prefix increment or decrement operator shall have atomic, qualified, or unqualified real or pointer type, and shall be a modifiable lvalue.

Therefore you can do:

p_Buf = ( uint8_t* )p_Buf + 1 ;

Where p_Buf is lvalue and ( uint8_t* )p_Buf is rvalue.


Let me just note that in your second example you don't cast( as you said ), but you declare a uint8_t pointer. Then when you use ++ on it you don't perform any casts( because it has the correct type ) and the operation is valid.

Upvotes: 5

sfstewman
sfstewman

Reputation: 5677

The answer @2501 posted is absolutely correct, but doesn't explain why the standard requires an lvalue for post-increment. The basic reason is that you need an lvalue (variable or memory location) for post-increment to perform its increment.

When you cast p_Buf to an uint8_t* type, you've created an rvalue in C. In simple terms, rvalues represent transient values that can be passed to functions, assigned to variables, etc. Post-increment returns the original value and then updates the variable or memory location where the value was stored, incrementing it. Because they only exist for the duration of the expression, rvalues cannot be updated, and post-increment cannot operate on them. Thus the problem with

    otherFunc (((uint8_t *)p_Buf)++); //error = expression must be a modifiable lvalue

is that the ((uint8_t *)pBuf) is just an rvalue expression with no actual storage location. In effect, the cast means that you're only using the value of p_Buf, and no longer directly using the variable p_Buf.

On the other hand, when you assign the cast to a variable:

p_Buf_8bit = (uint8_t *) p_Buf;

then the variable p_Buf_8bit is an lvalue, which represent a variable or memory location. This can be post-incremented, making this a perfectly well-formed statement in C:

    otherFunc (p_Buf_8bit++); 

Upvotes: 1

Vladyslav
Vladyslav

Reputation: 786

Incrementing a void pointer in C is bad idea. Most compilers don't even let it to be compiled. Use this instead:

p_Buf = (uint8_t*)p_Buf + 1;

Upvotes: 1

Carl Norum
Carl Norum

Reputation: 224864

The result of a cast operation is an rvalue, not an lvalue. ((uint8_t *)p_Buf)++ is simply not legal C code.

Upvotes: 0

Related Questions