expression式“j = ++(i | i); j = ++(i&i);应该是一个左值错误?

我期待在我的下面的代码:

#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; } 

expression式j = ++(i | i);j = ++(i & i); 会产生如下的左值错误:

 xc: In function 'main': xc:6: error: lvalue required as increment operand xc:9: error: lvalue required as increment operand 

但是我惊讶的发现上面的代码编译成功了,如下所示:

 ~$ gcc xc -Wall ~$ ./a.out 11 11 12 12 

检查上面的代码正常工作 。

而其他运营商产生的错误(据我所知)。 即使按位运算符XOR导致错误j = ++(i ^ i); (在编译时检查其他运算符是否产生左值错误 )。

是什么原因? 这是不明确的还是未定义的? 或按位或运算符是不同的?

编译器版本:

 gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5) 

但是我相信编译器版本不应该引起非一致的行为。 如果^没有编译,那么| &也没有。 否则应该为所有人工作

在c99模式下,这个编译器没有错误: gcc xc -Wall -std=c99

你是对的,它不应该编译,在大多数编译器,它不编译。
(请具体说明哪个编译器/版本不会给你编译器错误)

我只能假设编译器知道(i | i) == i(i & i) == i身份,并使用这些身份来优化expression式,只留下variablesi

这只是一个猜测,但对我来说很有意义。

这是一个已经在最近的GCC版本中解决的错误。

这可能是因为编译器优化i & i ii | i i | ii 。 这也解释了为什么xor操作员不工作; i ^ i会优化为0 ,这不是一个可修改的左值。

C11(n1570),§6.5.3.1前缀增量和减量运算符
前缀增量或减量运算符的操作数应该是primefaces的,限定的或不合格的实数或指针types,并且应该是可修改的左值

C11(n1570),§6.3.2.1左值,数组和函数指示符
一个可修改的左值是一个左值,没有数组types,没有不完整的types,没有const限定的types,如果它是一个结构或联合,没有任何成员(包括recursion,任何成员或所有包含的聚合或联合的元素)与一个const限定types。

C11(n1570),第6.3.2.1节左值,数组和函数指示符
左值是一个expression式(具有非void的对象types),可以指定一个对象。

C11(n1570),§3.术语,定义和符号
对象:执行环境中的数据存储区域,其内容可以表示值

据我所知, 可能意味着“能够存在但还没有存在”。 但是(i | i)不能在执行环境中引用区域作为数据存储。 因此它不是一个左值。 这似乎是一个旧的海湾合作委员会版本中的错误,自此以后修复。 更新你的编译器!

只是我的问题的后续行动。 我添加了详细的答案,以便人们可以find有用的。

在我的代码expression式j = ++(i | i); j = ++(i & i); 是不是左值错误?

因为@abelenky的编译器优化回答(i | i) == i(i & i) == i 。 这是正确的。

在我的编译器(gcc version 4.4.5) ,任何包含单个variables和结果的expression式都是不变的; 优化成一个单一的variables(不称为expression式 )。

例如:

 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 

在上面的汇编代码中,所有的expression式转换成以下代码

 movl 28(%esp), %eax movl %eax, 24(%esp) 

这相当于C代码中的j = i 。 因此j = ++(i | i);j = ++(i & i); 被优化为j = ++i

注意: j = (i | i)是一个语句,其中,expression式(i | i) 不是C中的语句(nop)

因此我的代码可以成功编译。

为什么j = ++(i ^ i); 或者j = ++(i * i); j = ++(i | k); 在我的编译器上产生左值错误?

因为任何一个expression式都有固定值或者不可修改的左值(未优化的expression式)。

我们可以使用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编译器(或大多数编译器)产生一个左值错误?

因为expression式++(i | i)++(i & i)求值禁止增量(++)运算符的实际定义。

根据丹尼斯·M·里奇(Dennis M. Ritchie)在“2.8递增和递减操作符”一节第44页中的“ The C Programming Language ”一书

增量和减量运算符只能应用于variables; 像(i + j)++这样的expression式是非法的。 操作数必须是算术或指针types的可修改左值。

我在新的gcc编译器4.47testing它在这里产生错误,因为我期待。 我也testing了tcc编译器。

任何对此的反馈/意见将是伟大的。

我根本不认为这是一个优化错误,因为如果是这样的话,那么首先应该不会有任何错误。 如果++(i | i)优化为++(i) ,那么不应该有任何错误,因为(i)是一个左值。

恕我直言,我认为,编译器看到(i | i)作为expression式输出,显然,输出右值,但增量运算符++期望左值改变它,因此错误。