Gri*_*han 44 c optimization gcc pre-increment gcc4.4
我期待以下代码:
#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;
}
Run Code Online (Sandbox Code Playgroud)
表情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
Run Code Online (Sandbox Code Playgroud)
但我惊讶于上面的代码编译成功,如下所示:
~$ gcc x.c -Wall
~$ ./a.out
11 11
12 12
Run Code Online (Sandbox Code Playgroud)
而其他运营商产生错误(据我所知).甚至按位运算符XOR导致错误j = ++(i ^ i);(检查其他运算符在编译时产生左值错误).
是什么原因?这是未指定还是未定义?或按位OR AND运算符是不同的?
编译器版本:
gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)
Run Code Online (Sandbox Code Playgroud)
但我认为编译器版本不应该导致非均匀行为.如果^没有编译,然后|与&还没有.否则应该适用于所有人
在c99模式下,这个编译器不是错误:gcc x.c -Wall -std=c99.
abe*_*nky 28
你是对的,它不应该编译,并且在大多数编译器上,它不编译.
(请准确说明哪个编译器/版本没有给您编译错误)
我只能假设,编译器知道的身份(i | i) == i和(i & i) == i并使用这些身份优化掉的表情,只留下后面的变量i.
这只是一个猜测,但它对我来说很有意义.
ant*_*ijn 25
这是一个已在最近的GCC版本中解决的错误.
这可能是因为编译器优化i & i来i和i | i到i.这也解释了为什么xor运算符不起作用; i ^ i将被优化为0,这不是可修改的左值.
md5*_*md5 17
C11(n1570),第6.5.3.1节前缀增量和减量运算符
前缀增量或减量运算符的操作数应具有原子,限定或非限定的实数或指针类型,并且应为可修改的左值.C11(n1570),第6.3.2.1节左值,数组和函数指示符
可修改的左值是一个左值,它没有数组类型,没有不完整的类型,没有常量类型,如果它是结构或联合,没有任何成员(包括,递归地,所有包含的聚合或联合的任何成员或元素)具有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
Run Code Online (Sandbox Code Playgroud)
==> 手段 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);
}
Run Code Online (Sandbox Code Playgroud)
汇编输出:( 阅读评论)
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
Run Code Online (Sandbox Code Playgroud)
在上面的汇编代码中,所有表达式都转换为以下代
movl 28(%esp), %eax
movl %eax, 24(%esp)
Run Code Online (Sandbox Code Playgroud)
这相当于j = iC代码.因此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;
}
Run Code Online (Sandbox Code Playgroud)
汇编代码:( 阅读评论)
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
Run Code Online (Sandbox Code Playgroud)
因此,这产生了一个lvalue error因为操作数不是可修改的左值.和非均匀的行为是由于GCC-4.4编译器优化.
为什么新的gcc编译器(或大多数编译器)会产生左值误差?
因为表达式的评估++(i | i)并++(i & i)禁止增量(++)运算符的实际定义.
根据Dennis M. Ritchie 在"2.8增量和减量运算符"第44页的" C语言编程 " 一书中的说法.
递增和递减运算符只能应用于变量; 像(i + j)++这样的表达式是非法的.操作数必须是算术或指针类型的可修改左值.
我测试了新的gcc编译器4.47,它产生的错误正如我所期待的那样.我还测试了tcc编译器.
对此的任何反馈/评论都会很棒.
| 归档时间: |
|
| 查看次数: |
3696 次 |
| 最近记录: |