只是我的问题的后续行动。我添加了详尽的答案,以便人们发现它很有帮助。
在我的代码表达式中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代码:( 阅读评论)
#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);
}
汇编输出:(阅读评论)
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)
这相当于j = i
在 C 代码中。因此j = ++(i | i);
和j = ++(i & i);
被优化为j = ++i
。
注意: j = (i | i)
是一个语句,其中 as 表达式(i | i)
不是 C 中的语句 (nop)
因此我的代码可以成功编译。
为什么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;
}
汇编代码:(阅读评论)
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)
禁止增量(++)运算符的实际定义。
根据 Dennis M. Ritchie 的书“ The C Programming Language ”在“2.8 Increment and Decrement Operators”第 44 页。
递增和递减运算符只能应用于变量;像 (i+j)++ 这样的表达式是非法的。操作数必须是算术或指针类型的可修改左值。
我在这里测试了新的gcc 编译器 4.47,它产生了我所期望的错误。我还在tcc 编译器上进行了测试。
对此的任何反馈/评论都会很棒。