我期待,在我下面的代码:
#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);
return 1;
}
表达式j = ++(i | i);
和j = ++(i & i);
会产生左值误差如下:
x.c: In function ‘main’:
x.c:6: error: lvalue required as increment operand
x.c:9: error: lvalue required as increment operand
但我惊讶的是上面的代码编译成功,如下图所示:
~$ gcc x.c -Wall
~$ ./a.out
11 11
12 12
检查上面的代码工作正常。
而其他运营商产生错误(据我所知)。 一个错误的偶数位运算符XOR原因j = ++(i ^ i);
(检查其他运营商产生一个左值误差在编译时间)。
是什么原因? 这是不确定或不明确? 或按位OR和运营商有什么不同?
编译器版本:
gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)
但我相信编译器版本不应该理性的非均匀行为。 如果^
不那么编译|
和&
也没有。 否则应适用于所有
它不是用这个编译器C99模式下的错误: gcc xc -Wall -std=c99
。
C11(n1570),第6.5.3.1前缀增量和减量运算符
前缀增量或减量运算符的操作数应具有原子,合格,不合格的或真实或指针类型,并应修改的左值 。
C11(n1570),§6.3.2.1左值,数组和功能指示器
修改的左值是不具有阵列型左值,不具有不完整的类型,不具有const-限定类型,并且如果它是一个结构或联合,没有任何构件(包括,递归,任何部件或所有的元件包含具有const-限定类型的聚集体或联合)。
C11(n1570),§6.3.2.1左值,数组和功能指示器
左值是表达式(比其他的对象类型void
),其潜在地指定的对象。
C11(n1570),第3术语,定义和符号
对象:在执行环境中的数据存储的区域中,其内容可以代表的数值
据我所知,这可能意味着“能够被但尚未存在”。 但(i | i)
不能够参考的区域在执行环境中的数据存储的。 因此,它不是一个左值。 这似乎是在一个旧的gcc版本中的错误,因为固定。 更新你的编译器!
只是跟进我的问题。 我加了精心的答案,使人们可以发现它有用。
在我的代码表达式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)
是一个语句作为在那里表达(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)
禁止增量(++)操作者的实际认定中。
据丹尼斯·里奇米的书“ C程序设计语言 ”一节“2.8递增和递减运算符”第44页。
递增和递减运算符只能应用于变量; 像(I + J)的表达式++是非法的。 的操作数必须是算术或指针类型的修改的左值。
我对新的受测试gcc编译器4.47在这里它作为我期待产生错误。 我还测试了TCC的编译器。
任何反馈/关于这个意见将是巨大的。
我不认为在所有它是一个优化的错误,因为如果是这种情况,不应该有摆在首位任何错误。 如果++(i | i)
进行了优化, ++(i)
,那么不应该有任何错误,因为(i)
是一个左值。
IMHO,我认为编译器看到(i | i)
作为表达输出,即,很明显,输出右值,但递增运算符++
预计左值来改变它,从而该错误。