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
i
和i | i
i | i
到i
。 这也解释了为什么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式输出,显然,输出右值,但增量运算符++
期望左值改变它,因此错误。