只是对我的问题的跟进。我添加了详尽的答案,以便人们发现它很有帮助。
在我的代码表达式中j = ++(i | i); 和j = ++(i & i); 不是左值错误引起的?
因为@abelenky 回答了(i | i) == i 和(i & i) == i 的编译器优化。那是完全正确的。
在我的编译器(gcc version 4.4.5) 中,任何包含单个变量和结果的表达式都不会改变;优化为单个变量(称为不是表达式)。
例如:
j = i | i ==> j = i
j = i & i ==> j = i
j = i * 1 ==> j = i
j = i - i + i ==> j = i
==> 表示optimized to
为了观察它,我编写了一个小的 C 代码并使用 gcc -S 反汇编它。
C 代码:(读取 cmets)
#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);
}
汇编输出:(读取 cmets)
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
在上面的汇编代码中,所有的表达式都转换成下面的代码:
movl 28(%esp), %eax
movl %eax, 24(%esp)
相当于 C 代码中的j = i。因此j = ++(i | i); 和j = ++(i & i); 被优化为j = ++i。
注意: j = (i | i) 是一个语句,其中表达式为 (i | i) not a statement (nop) in C
因此我的代码可以成功编译。
为什么 j = ++(i ^ i); 或 j = ++(i * i); , j = ++(i | k); 在我的编译器上产生左值错误?
因为任一表达式具有常量值或不可修改的左值(未优化的表达式)。
我们可以使用asm代码观察
#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;
}
汇编代码:(读取 cmets)
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
因此这会产生一个lvalue error,因为操作数不是可修改的左值。而非统一行为是由于 gcc-4.4 中的编译器优化造成的。
为什么新的 gcc 编译器(或大多数编译器)会产生左值错误?
因为表达式++(i | i) 和++(i & i) 的评估禁止实际定义increment(++) 运算符。
根据 Dennis M. Ritchie 的书“The C Programming Language”在 “2.8 增量和减量运算符”第 44 页。
递增和递减运算符只能应用于变量;像 (i+j)++ 这样的表达式是非法的。操作数必须是算术或指针类型的可修改左值。
我在新的gcc compiler 4.47 here 上进行了测试,它产生了我所期望的错误。
我也tested on tcc compiler.
任何关于此的反馈/cmets 都会很棒。