Why do I get the message "invalid lvalue in unary '&'"?

Previous The C Language Next

Q: Well, I need to know more about lvalues and GNU C initializers and cast constructors. Especially, I need to know why I sometimes get an error message "invalid lvalue in unary '&'"...
A: Click here to see everything about GNU C extensions. But if you are lazy to read too much, I will be more concrete here. First, in GNU C (TIGCC is GNU C), initializers may be non-constants. For example,
int a = 3, b = 4, c = 5, d = 6;
int array[4] = {a, b, c, d};
is quite legal. That's why
int a = 3, b = 4, c = 5, d = 6;
SCR_RECT myScr = {{b + a, b - a, d + c, d - c}};
is quite legal too. Second, GNU C has one very nice extension in addition to ordinary C: cast constructors. This is a method for constructing structures, arrays, unions etc. "on fly" by using a typecasting of an initializer to an appropriate data type, for example
(SCR_RECT){{10, 10, 50, 50}}
So, you can use
SCR_RECT myScr;
...
myScr = (SCR_RECT){{10, 10, 50, 50}};
which is impossible in ordinary C (ANSI C). You can even use
myScr = (SCR_RECT){{a, b, c, d}};
where a,b,c,d are expressions. Well, but what is now the problem? See, C has two type of objects: lvalues and non-lvalues. lvalues are objects which may appear on the left size of an assignment. For example, a variable is an lvalue and a constant is not an lvalue, because 'x=5' is legal and '5=x' (or '5=3') is not legal. Not only variables are lvalues; for example, dereferenced pointers are also lvalues, so this is legal for example (store 100 at address 0x4c00):
*(char*)0x4c00 = 100;
So, '*(char*)0x4c00' is an lvalue. Now, about the problem. In GNU C, cast constructors are lvalues only if the initializer is completely constant. I.e. '(SCR_RECT){{10,10,50,50}}' is an lvalue, but '(SCR_RECT){{a,b,c,d}}' is not. As C language accepts unary '&' operator (i.e. "address of") only on lvalue objects, this means that, for example,
&(SCR_RECT){{10, 10, 50, 50}}
is legal, but
&(SCR_RECT){{a, b, c, d}}
is not! This is the real cause of the problem!!!

What you can do if you need an address of non-constant cast constructor? You need to declare an auxilary variable. For example, declare one SCR_RECT variable, say myScr,
SCR_RECT myScr;
and instead of
ScrRectFill (&(SCR_RECT){{a, b, c, d}}, ScrRect, A_XOR);
use:
myScr = (SCR_RECT){{a, b, c, d}};
ScrRectFill (&myScr, ScrRect, A_XOR);
Note that '&myScr' is legal, because 'myScr' is an lvalue (it is an ordinary variable). I hope that this helps a lot understanding of cast constructors and lvalues.