4

ここにいる誰もが知っていると思いますが、これ--iは左の値式でi--あり、右の値式です。しかし、2 つの式のアセンブリ コードを読んだところ、同じアセンブリ コードにコンパイルされていることがわかりました。

mov eax,dword ptr [i]
sub eax,1
mov dword ptr [i],eax

C99 言語標準では、オブジェクト型または void 以外の不完全な型を持つ式に左辺値が定義されます。

したがって--i、void 以外の型のi--値を返す一方で、void または一時変数の値を返すようにすることができます。

ただし、 のような割り当てを行うi--=5と、コンパイラは が左辺値ではないことを示すエラーを表示しi--ます。そうでない理由と、戻り値が一時変数である理由がわかりません。コンパイラはどのようにそのような判断を下すのでしょうか? アセンブリ言語レベルで説明してもらえますか?ありがとう!

4

3 に答える 3

8

左の値?正しい値?

左辺値右辺値について話している場合、左辺値または右辺値であるというプロパティは式の結果に適用されます。つまり、およびの結果を考慮する必要が--iありi--ます。また、C 言語では、との両方 が右辺値です。したがって、あなたの質問は、C 言語の領域における誤った前提に基づいています。どちらも左辺値ではないことが明確に述べられているため、C99標準を参照してどの点を指摘しようとしているのかわかりませんまた、 a を返すことの意味が明確ではありません。いいえ、組み込みの接尾辞は を返しません。--ii----ii--void--void

--iとの場合の左辺値と右辺値の違いはi--、C++ にのみ存在します。

いずれにせよ、単なる--i;andi--;式のステートメントを見ているのであれば、これらの式の結果を使用していません。あなたはそれらを捨てています。スタンドアロンを使用する唯一のポイント--ii--、その副作用 (の減少i) です。しかし、それらの副作用は同じであるため、生成されるコードは同じであることが完全に予想されます。

--iとの式の違いを確認したい場合はi--、それらの結果を使用する必要があります。例えば

int a = --i;
int b = i--;

初期化ごとに異なるコードを生成します。

ただし、この例は、結果の左辺値または右辺値とは何の関係もありません。その側(上で述べたように、C++にのみ存在する)との違いを観察したい場合は、これを試すことができます

int *a = &--i;
int *b = &i--;

最初の初期化は C++ でコンパイルされますが (結果が左辺値であるため)、2 番目の初期化はコンパイルされません (結果が右辺値であり、組み込み単項を右辺値に適用できないため&)。

この仕様の背後にある理論的根拠はかなり明白です。は の新しい値に評価--iされるため、この演算子がその結果として自身への参照を返すようにすることは完全に可能です(また、C とは対照的に、C++ 言語は可能な限り左辺値を返すことを好みます)。一方、の古い値を返す必要があります。結果を分析するまでには、それ自体が新しい値を保持している可能性が高いため、 への参照を返すことはできません。の古い値を補助的な一時的な場所に保存 (または再作成) し、次の結果として返す必要があります。iii--ii--iiii--. その一時的な値は単なる値であり、オブジェクトではありません。メモリに常駐する必要がないため、左辺値にすることはできません。

于 2012-12-21T03:17:03.760 に答える
1

[注: C++ の観点からこれに答えています。]

iが組み込み型であると仮定すると、たとえば--i;ori--;ではなくj = ++i;orを記述j = i++;した場合、コンパイラによってアセンブリ コードにコンパイルされることは驚くべきことではありませんi。違いが明らかになるのは、アセンブリ レベルで結果を処理する場合のみです。それ以外の場合は、実質的に同じセマンティクスを持ちます。

(ユーザー定義型のオーバーロードされたプリデクリメント演算子とポストデクリメント演算子について考えていた場合、生成されるコードは同じではないことに注意してください。)

のようなものを書くとi-- = 5;、コンパイラはまったく当然のことながら文句を言います。ポストデクリメントのセマンティクスは本質的に問題のものをデクリメントするが、さらに使用するために古い値を返すためです。返されるものは一時的なものになるため、なぜi--r値が得られるのですか。

于 2012-12-21T03:17:33.673 に答える
0

「左辺値」および「右辺値」という用語は、代入式に由来します。この式E1 = E2では、左のオペランドE1を使用して変更するオブジェクトを識別し、右のオペランドE2を使用して使用する値を識別します。(C 1999 6.3.2.1、注53を参照してください。)

したがって、まだ何らかのオブジェクトが関連付けられている式を使用して、そのオブジェクトを見つけて書き込むことができます。これは左辺値です。式が左辺値でない場合は、右辺値と呼ばれることがあります。

たとえば、iあるオブジェクトの名前に、がある場合、それは左辺値です。これは、がどこにあるかを見つけることがiでき、のようにそれに割り当てることができるためi = 3です。

一方、式がある場合はi+1、iの値を取得して1を加算し、値を取得しましたが、特定のオブジェクトに関連付けられていません。この新しい値はにありませんi。これは一時的な値であり、特定の場所はありません。(確かに、最適化によって式が完全に削除されない限り、コンパイラはそれをどこかに配置する必要があります。ただし、レジスタにあり、メモリにはない可能性があります。何らかの理由でメモリにある場合でも、C言語は方法を提供しません。i+1割り当ての左側では使用できないため、左辺値ではありません。

--ii++は両方とも、の値を取り、iいくつかの算術を実行した結果として生じる式です。(これらの式も変更されますiが、これは演算子の副作用であり、返される結果の一部ではありません。)左辺値と右辺値の「左」と「右」は、演算子が左側にあるかどう--かとは関係ありません。++または名前の右側。それらは、割り当ての左側または右側と関係があります。他の回答が説明しているように、C ++では、左辺値の左側にある場合、左辺値を返します。ただし、これは偶然です。C ++での演算子のこの定義は、「左辺値」という用語の作成から何年も経ってから来ました。

于 2012-12-21T04:39:21.653 に答える