私の質問へのフォローアップです。参考になるように、精巧な回答を追加しました。
私のコード式j = ++(i | i);
で j = ++(i & i);
は、左辺値エラーの原因ではありませんか?
@abelenkyが答えたようにコンパイラの最適化の(i | i) == i
ため(i & i) == i
. それはまさに正しいです。
私のコンパイラ(gcc version 4.4.5)
では、単一の変数と結果を含む式は変更されません。単一の変数 ( not an 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
上記のアセンブリ コードでは、すべての式が次のコードに変換されます。
movl 28(%esp), %eax
movl %eax, 24(%esp)
j = i
これはC コードと同等です。したがってj = ++(i | i);
、 およびj = ++(i & i);
は に最適化されj = ++i
ます。
注意: j = (i | i)
は as 式(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)
Dennis M. Ritchie の著書「The C Programming Language」のセクション「2.8 Increment and Decrement Operators」44 ページによると。
インクリメントおよびデクリメント演算子は、変数にのみ適用できます。(i+j)++ のような式は不正です。オペランドは、算術型またはポインター型の変更可能な左辺値でなければなりません。
ここで新しい gcc コンパイラ 4.47 でテストしたところ、予想どおりエラーが発生しました。tcc コンパイラでもテストしました。
これに関するフィードバック/コメントは素晴らしいでしょう。