Wyrażenia " j = ++(i | I); and j = ++(i & I); should be a lvalue error?

Spodziewałem się tego w moim następującym kodzie:

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

Wyrażenia j = ++(i | i); i j = ++(i & i); spowodują błędy lvalue jak poniżej:

x.c: In function ‘main’:
x.c:6: error: lvalue required as increment operand
x.c:9: error: lvalue required as increment operand   

Ale dziwi mnie, że powyższy kod skompilowany pomyślnie, jak poniżej:

~$ gcc x.c -Wall
~$ ./a.out 
11 11
12 12   

Sprawdź powyższy kod działa poprawnie.

Podczas gdy inni operatorzy produkują błąd (jak rozumiem). Nawet Operator bitowy XOR powoduje błąd j = ++(i ^ i); (sprawdź inne operatory wytwarzają błąd lvalue w czasie kompilacji).

Jaki jest powód? To jest nieokreślone czy nieokreślone ? czy operatory or I bitowe są różne?

Wersja kompilatora:

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

ale uważam, że wersja kompilatora nie powinna powodować niejednorodnego zachowania. If ^ not compiled then | and & also not. w przeciwnym razie powinno działać dla wszystkich

To nie jest błąd z tym kompilatorem w trybie c99: gcc x.c -Wall -std=c99.

Author: Grijesh Chauhan, 2013-02-13

5 answers

Masz rację, że nie powinien kompilować, a na większości kompilatorów nie kompiluje.
(proszę dokładnie określić, który kompilator / wersja nie powoduje błędu kompilatora)

Mogę tylko postawić hipotezę, że kompilator zna tożsamości (i | i) == i i (i & i) == i i używa tych tożsamości do optymalizacji wyrażenia, pozostawiając tylko zmienną i.

To tylko przypuszczenie, ale ma dla mnie sens.
 28
Author: abelenky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-13 18:26:01

Jest to błąd, który został rozwiązany w nowszych wersjach GCC.

Prawdopodobnie dlatego, że kompilator optymalizuje i & i do i i i | i do i. To wyjaśnia również, dlaczego operator xor nie działał; i ^ i byłby zoptymalizowany do 0, co nie jest modyfikowalnym lvalue.

 25
Author: antonijn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-13 18:28:55

C11 (n1570), § 6.5.3.1 operatory przyrostowe i przyrostowe
Operand przedrostka increment lub decrement operator ma atomic, qualified, lub niewykwalifikowanym typem rzeczywistym lub wskaźnikowym i musi być modyfikowalnym lvalue .

C11 (n1570), § 6.3.2.1 Lvalues, arrays, and function designators
Modyfikowalne lvalue to lvalue, które nie ma typu tablicy, nie ma typu niekompletnego, nie ma const- Typ kwalifikowany, a jeśli jest strukturą lub związkiem, nie ma żadnego członka (w tym, rekurencyjnie, każdy członek lub element wszystkich zawartych agregatów lub związków) z const- kwalifikowany Typ.

C11 (n1570), § 6.3.2.1 Lvalues, arrays, and function designators
Lvalue jest wyrażeniem (z typem obiektu innym niż void), które potencjalnie wyznacza obiekt.

C11 (n1570), § 3. Terminy, definicje i symbole
Obiekt: Region przechowywania danych w środowisku wykonawczym, którego zawartość może reprezentować wartości

Z tego co wiem, potencjalnie oznacza "zdolny do bycia, ale jeszcze nie istnieje". Ale (i | i) nie jest w stanie odwoływać się do regionu przechowywania danych w środowisku wykonawczym. Dlatego nie jest to lvalue. Wydaje się, że jest to błąd w starej wersji gcc, naprawiony od tego czasu. Zaktualizuj swój kompilator!

 17
Author: md5,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-16 14:03:16

To tylko kontynuacja mojego pytania . Dodałem rozbudowaną odpowiedź, aby można było ją znaleźć pomocną.

W moim kodzie wyrażenia j = ++(i | i); i j = ++(i & i); nie są spowodowane błędem lvalue ?

Z powodu optymalizacji kompilatora, na co @abelenky odpowiedział (i | i) == i i (i & i) == i. Dokładnie tak.

W moim kompilatorze (gcc version 4.4.5) każde wyrażenie zawierające pojedynczą zmienną i wynik jest niezmienione; zoptymalizowane do pojedynczej zmiennej (coś o nazwie nie wyrażenie ).

Na przykład:

j = i | i      ==> j = i
j = i & i      ==> j = i
j = i * 1      ==> j = i
j = i - i + i  ==> j = i 

==> środki optimized to

Aby go obserwować napisałem mały kod C i zdemontowałem go za pomocą gcc -S.

Kod C: (czytaj komentarze )

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

Wyjście montażowe: ( czytaj komentarze )

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  

W powyższym kodzie złożenia wszystkie wyrażenia zamienione na następujący kod:

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

To odpowiednik j = i w kodzie C. Tak więc j = ++(i | i); i j = ++(i & i); są zoptymalizowane do j = ++i.

zawiadomienie: j = (i | i) jest stwierdzeniem, gdzie jako wyrażenie (i | i) not a statement (nop) in C

Stąd mój kod mógł się pomyślnie skompilować.

Dlaczego j = ++(i ^ i); lub j = ++(i * i); , j = ++(i | k); produkować błąd lvalue na moim kompilatorze?

Ponieważ albo wyrażenie ma wartość stałą, albo nie można modyfikować lvalue (wyrażenie nieoptymalizowane).

możemy obserwować za pomocą asm kod

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

Kod zestawu: ( czytaj komentarze )

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

Stąd więc to tworzy lvalue error, ponieważ operand nie jest modyfikowalnym lvalue. I niejednorodne zachowanie wynika z optymalizacji kompilatora w gcc-4.4.

Dlaczego nowe Kompilatory gcc (lub większość kompilatorów) generują błąd lvalue?

Ponieważ ocena wyrażenia ++(i | i) i ++(i & i) zabrania rzeczywistego definacji operatora increment (++).

Według książki Dennisa M. Ritchie "język programowania C" w sekcji " 2.8 operatory przyrostowe i przyrostowe" strona 44.

Operatory increment i decrement mogą być stosowane tylko do zmiennych; wyrażenie takie jak (i + j)++ jest nielegalne. Operand musi być modyfikowanym lvalue typu arytmetycznego lub wskaźnika.

Testowałem na nowym kompilatorze gcc 4.47 tutaj generuje błąd tak, jak się spodziewałem. I również testowane na kompilatorze tcc.

Wszelkie opinie/ komentarze na ten temat byłyby świetne.

 7
Author: Grijesh Chauhan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:28

W ogóle nie uważam, że jest to błąd optymalizacji, bo jeśli tak, to w ogóle nie powinno być żadnego błędu. Jeśli ++(i | i) jest zoptymalizowane do ++(i), to nie powinno być żadnego błędu, ponieważ (i) jest lvalue.

IMHO, myślę, że kompilator widzi {[3] } jako wyjście wyrażenia, które oczywiście wyprowadza rvalue, ale operator Przyrostowy ++ oczekuje, że lvalue ją zmieni, a więc błąd.

 1
Author: Ghasan Al-Sakkaf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-16 14:34:03