Just a follow-up to my question . I added elaborate answer so that one can find it helpful.
In my code expressions j = ++(i | i);
and j = ++(i & i);
are not caused for lvalue error ?
Because of compiler optimization as @abelenky answered (i | i) == i
and (i & i) == i
. That is exactly CORRECT.
In my compiler (gcc version 4.4.5)
, any expression that includes single variable and result is unchanged; optimized into a single variable (something called not an expression).
for example:
j = i | i ==> j = i
j = i & i ==> j = i
j = i * 1 ==> j = i
j = i - i + i ==> j = i
==>
means optimized to
To observe it I written a small C code and disassemble that with gcc -S
.
C-Code: (read comments)
#include<stdio.h>
int main(){
int i = 10;
int j = 10;
j = i | i; //==> j = i
printf("%d %d", j, i);
j = i & i; //==> j = i
printf("%d %d", j, i);
j = i * 1; //==> j = i
printf("%d %d", j, i);
j = i - i + i; //==> j = i
printf("%d %d", j, i);
}
assembly output: (read comments)
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $10, 28(%esp) // i
movl $10, 24(%esp) // j
movl 28(%esp), %eax //j = i
movl %eax, 24(%esp)
movl $.LC0, %eax
movl 28(%esp), %edx
movl %edx, 8(%esp)
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl 28(%esp), %eax //j = i
movl %eax, 24(%esp)
movl $.LC0, %eax
movl 28(%esp), %edx
movl %edx, 8(%esp)
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl 28(%esp), %eax //j = i
movl %eax, 24(%esp)
movl $.LC0, %eax
movl 28(%esp), %edx
movl %edx, 8(%esp)
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl 28(%esp), %eax //j = i
movl %eax, 24(%esp)
movl $.LC0, %eax
movl 28(%esp), %edx
movl %edx, 8(%esp)
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
In above assembly code all expressions converted into following code:
movl 28(%esp), %eax
movl %eax, 24(%esp)
that is equivalent to j = i
in C code. Thus j = ++(i | i);
and and j = ++(i & i);
are optimized to j = ++i
.
Notice: j = (i | i)
is a statement where as expression (i | i)
not a statement (nop) in C
Hence my code could successfully compiled.
Why j = ++(i ^ i);
or j = ++(i * i);
, j = ++(i | k);
produce lvalue error on my compiler?
Because either expression has the constant value or not modifiable lvalue (unoptimized expression).
we can observe using asm
code
#include<stdio.h>
int main(){
int i = 10;
int j = 10;
j = i ^ i;
printf("%d %d\n", j, i);
j = i - i;
printf("%d %d\n", j, i);
j = i * i;
printf("%d %d\n", j, i);
j = i + i;
printf("%d %d\n", j, i);
return 1;
}
assembly code: (read comments)
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $10, 28(%esp) // i
movl $10, 24(%esp) // j
movl $0, 24(%esp) // j = i ^ i;
// optimized expression i^i = 0
movl $.LC0, %eax
movl 28(%esp), %edx
movl %edx, 8(%esp)
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, 24(%esp) //j = i - i;
// optimized expression i - i = 0
movl $.LC0, %eax
movl 28(%esp), %edx
movl %edx, 8(%esp)
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl 28(%esp), %eax //j = i * i;
imull 28(%esp), %eax
movl %eax, 24(%esp)
movl $.LC0, %eax
movl 28(%esp), %edx
movl %edx, 8(%esp)
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl 28(%esp), %eax // j = i + i;
addl %eax, %eax
movl %eax, 24(%esp)
movl $.LC0, %eax
movl 28(%esp), %edx
movl %edx, 8(%esp)
movl 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $1, %eax
leave
Hence so this produce an lvalue error
because operand is not a modifiable lvalue. And non-uniform behavior is due to compiler optimization in gcc-4.4.
Why new gcc compliers(or most of compilers) produce an lvalue error?
Because evaluation of expression ++(i | i)
and ++(i & i)
prohibits actual defination of increment(++) operator.
According to Dennis M. Ritchie's book "The C Programming Language" in section "2.8 Increment and Decrement Operators" page 44.
The increment and decrement operators can only be applied to variables; an expression like (i+j)++ is illegal. The operand must be a modifiable lvalue of arithmetic or pointer type.
I tested on new gcc compiler 4.47 here it produces error as I was expecting.
I also tested on tcc compiler.
Any feedback/ comments on this would be great.
(i|i)
is expression not a variable see++(i|i)
something like(i|i) =
(i|i)` + 1` that is lvalue error. – Westerfield++(i|i)
and++(i&i)
should trigger semantic errors, so this is a bug in whatever compiler you're using. Bugs do not have to behave uniformly. – Gild