表达式\“J = ++(I | I);和j = ++(I&I);应该是一个左值错误?(Express

2019-07-19 20:35发布

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

#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

Answer 1:

你是正确的,它不应该编译,并在大多数编译器,它不编译。
(请准确指定编译器/版本是不是给你一个编译器错误)

我只能假设,编译器知道身份的是(i | i) == i(i & i) == i并使用这些身份优化掉的表情,只留下身后可变i

这只是一个猜测,但它使一个很大的意义对我来说。



Answer 2:

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

这可能是因为编译器优化i & iii | i i | ii 。 这也解释了为什么XOR运算没有工作; i ^ i会被优化,以0 ,这是不修改的左值。



Answer 3:

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版本中的错误,因为固定。 更新你的编译器!



Answer 4:

只是跟进我的问题。 我加了精心的答案,使人们可以发现它有用。

在我的代码表达式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的编译器。

任何反馈/关于这个意见将是巨大的。



Answer 5:

我不认为在所有它是一个优化的错误,因为如果是这种情况,不应该有摆在首位任何错误。 如果++(i | i)进行了优化, ++(i) ,那么不应该有任何错误,因为(i)是一个左值。

IMHO,我认为编译器看到(i | i)作为表达输出,即,很明显,输出右值,但递增运算符++预计左值来改变它,从而该错误。



文章来源: Expressions \"j = ++(i | i); and j = ++(i & i); should be a lvalue error?