14

For example:

int x[100];
void *p;

x[0] = 0x12345678;
x[1] = 0xfacecafe;
x[3] = 0xdeadbeef;

p = x;
((int *) p) ++ ;

printf("The value = 0x%08x", *(int*)p);

Compiling the above generates an lvalue required error on the line with the ++ operator.

hopia
  • 4,880
  • 7
  • 32
  • 54

5 Answers5

20

The cast creates a temporary pointer of type int *. You can't increment a temporary as it doesn't denote a place to store the result.

In C and C++ standardese, (int *)p is an rvalue, which roughly means an expression that can only occur on the right-hand side of an assignment.

p on the other hand is an lvalue, which means it can validly appear on the left-hand side of an assignment. Only lvalues can be incremented.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 1
    In C++, lvalue and rvalue have very little to do with assignments. `array_name` is an lvalue that cannot occur on the left-hand side of an assignment, and `std::string("hello")` is an rvalue which can. – fredoverflow Mar 20 '11 at 08:11
  • @FredOverflow : Yes correct. BTW I added an answer addressing those issues. :) – Prasoon Saurav Mar 20 '11 at 08:21
2

The expression ((int *) p) treats the pointer stored inside the variable p is a pointer to int. If you want to treat the variable itself as a pointer to int variable (and then increment it), use a reference cast:

((int *&) p) ++ ;
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
2

Thanks to larsmans for pointing to the right direction.

I took the liberty of digging deeper into this. So for future reference, according to sections 6.5.2.4 and 6.5.4 of the C99 standard (http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf):

6.5.2.4 Postfix increment and decrement operators

Constraints

The operand of the postfix increment or decrement operator shall have qualified or unqualified real or pointer type and shall be a modifiable lvalue
..
..
6.5.4 Cast operators
..
..
[Footnote] 89) 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.

Note: This only applies to C. C++ may handle casts differently.

hopia
  • 4,880
  • 7
  • 32
  • 54
1

You can get the intended result with

p = (int*)p + 1;

Using the increment operator on a dereferenced pointer to p, which is an lvalue, also works:

(*(int**)&p)++;

However, the latter is not portable, since (void*)p might not have the same representation as (int*)p.

aaz
  • 5,136
  • 22
  • 18
  • Your first suggestion is correct; the second one is undefined behaviour (strict aliasing violation) – M.M Apr 05 '20 at 09:44
0

Rvalue expression ((int *) p) creates and temporary of type int* on which operator ++ cannot be applied.

++ requires an lvalue as its operand.

As @FredOverflow mentions lvalues and rvalues have very little to do with assignment.

Arrays are lvalues still they cannot be assigned to because they are non-modifiable. std::string("Prasoon") is an rvalue expression still it can occur on the left side of assignment operator because we are allowed to call member functions( operator = in this case) on temporaries.

Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
  • There are no scalar temporaries :) The result of evaluating the rvalue `((int *) p)` is just a value. – fredoverflow Mar 20 '11 at 08:28
  • @FredOverflow : I don't think the statement "There are no scalar temporaries" is 100% correct. What do you have to say about `const int& ref = int()`? Doesn't it create a scalar temporary? – Prasoon Saurav Mar 20 '11 at 09:27
  • `int()` is the same as `0`, but you have a point: binding to a reference-to-const can implicitly create a scalar temporary. How about this: "There is no *expression* whose result is a scalar temporary." – fredoverflow Mar 20 '11 at 10:15
  • @FredOverflow : `const int& ref = int()` as a whole is an expression, right? It can create a scalar temporary. So I've got problems with your second statement "There is no...." as well. :-) – Prasoon Saurav Mar 20 '11 at 10:18
  • @FredOverflow : Does the Standard explicitly say "you can't have a scalar temporary" ? I think no. – Prasoon Saurav Mar 20 '11 at 10:19
  • `const int& ref = int()` is not an expression, it's a definition. (If it were an expression, you could pass it as an argument to a function, for example.) – fredoverflow Mar 20 '11 at 10:22
  • @FredOverflow : Ah! Right! Missed that completely. Now I think I know what you think I know. :-) – Prasoon Saurav Mar 20 '11 at 10:25
  • Section 12.2 does not mention scalars explicitly, but it starts off with "Temporaries of *class type* are created in various contexts". I know, that does not mean much :) – fredoverflow Mar 20 '11 at 10:26
  • By the way, couldn't the compiler simply rewrite `const int& x = 0;` to `const int x = 0;` (without the reference), and the programmer could not tell a difference? – fredoverflow Mar 20 '11 at 10:27
  • @FredOverflow : I haven't seen the code that gets generated in both the case but the generated code in both the cases would pretty much be the same IMO. There is no need for that reference, right!! :) – Prasoon Saurav Mar 20 '11 at 10:30