C ++コンパイラは、不要な括弧を削除/最適化しますか?


19

コードは

int a = ((1 + 2) + 3); // Easy to read

より遅く走る

int a = 1 + 2 + 3; // (Barely) Not quite so easy to read

または、「無駄な」括弧を削除/最適化するのに十分な最新のコンパイラーです。

非常に小さな最適化の懸念のように思えるかもしれませんが、C#/ Java / ...よりもC ++を選択することは、最適化(IMHO)のすべてです。


9
C#とJavaもそれを最適化すると思います。ASTを解析して作成すると、そのような明らかな役に立たないものを削除するだけだと思います。
ファリッドノウリネシャット

5
私が読んだものはすべて、JITコンパイルが簡単に先行コンパイルをそのお金のために実行することを指しているので、それだけではあまり説得力のある議論ではありません。ゲームプログラミングを開始します-事前コンパイルを推奨する本当の理由は予測可能なことです-JITコンパイルでは、コンパイラーがいつコードをコンパイルして開始しようとするのかわかりません。しかし、私は、例えば、標準MLとD.を参照してください、ネイティブコードへの事前コンパイラは、ガベージコレクションと相互に排他的ではないことに注意してだろうと私は、ガベージコレクションが...より効率的であることを納得させるの引数を見てきました
Doval

7
... RAIIおよびスマートポインターよりも、そのため、これらの言語でゲームプログラミングを行うという比較的手に負えないパス(C ++)をたどるという点で問題が多くなっています。また、括弧の心配は正気ではないことに注意してください。どこから来たのかはわかりますが、それはばかげたマイクロ最適化です。プログラム内のデータ構造とアルゴリズムの選択は、パフォーマンスを支配するものであり、そのような些細なことではありません。
ドーバル

6
ええと... 正確にどのような最適化が期待されていますか?私が知っているほとんどの言語で静的分析について話している場合、これは静的に知られている結果に置き換えられます(LLVMベースの実装はこれを強制することもあります)。実行順序について話している場合、それは同じ操作で副作用がないため、問題ではありません。とにかく加算には2つのオペランドが必要です。そして、これを使用してパフォーマンスに関してC ++、Java、C#を比較する場合、最適化とは何か、そしてそれらがどのように機能するかについて明確な考えを持っていないように聞こえます。
テオドロスチャツィギアンナキス

5
なぜだろうかあなたは括弧に入れ式が読みやすく(私にはそれだけで(なぜ彼らがすべきことは、ここでassiciativeではない?この特定の順序を強調しない誤解を招く、醜い?)と不格好)考えるb)にあなたがすることなく、思うだろう理由括弧の方がパフォーマンスが向上する場合があります(明らかに、括弧の解析は、演算子の固定性について推論するよりもマシンにとって簡単です。Marcvan Leuwenが言うように、これはランタイムにまったく影響しません)。
左周り

回答:


87

コンパイラは実際に括弧を挿入または削除することはありません。それはあなたの表現に対応する解析木(括弧が存在しない)を作成するだけで、そうする際にはあなたが書いた括弧を尊重しなければなりません。式を完全に括弧で囲むと、その解析ツリーが何であるかがすぐに人間の読者に明らかになります。のように露骨に冗長な括弧を入れて極端に行くと、int a = (((0)));結果の解析ツリー(したがって生成されたコードを変更せずに、パーサーでいくつかのサイクルを浪費しながら、リーダーのニューロンにいくつかの無駄なストレスを引き起こします)ほんの少し。

括弧を記述しない場合でも、パーサーは構文解析ツリーを作成するという仕事をしなければならず、演算子の優先順位と結合性の規則は、どの構文解析ツリーを構築する必要があるかを正確に伝えます。これらのルールは、コードに挿入する(暗黙の)括弧をコンパイラに指示するものと考えるかもしれませんが、この場合、パーサーは実際に括弧を処理することはありません。特定の場所に存在していました。int a = (1+2)+3;(の関連性+は左にあります)のように、これらの場所に括弧を配置すると、パーサーはわずかに異なるルートで同じ結果に到達します。のように異なる括弧を入れた場合int a = 1+(2+3);その後、異なる解析ツリーを強制し、異なるコードが生成される可能性があります(ただし、結果ツリーを構築した後にコンパイラが変換を適用する可能性があるため、結果のコードを実行する効果が変わらない限り、それ)。再送信するコードに違いがあると仮定すると、一般的にはどちらがより効率的であるかを言うことはできません。もちろん、最も重要な点は、ほとんどの場合、解析ツリーは数学的に同等の式を提供しないため、実行速度を比較することはポイントの横にあります。適切な結果が得られる式を記述する必要があります。

つまり、正確さのために必要に応じて、そして読みやすさのために必要に応じて括弧を使用します。冗長な場合、それらは実行速度にまったく影響を与えません(そしてコンパイル時間にほとんど影響を与えません)。

また、これはいずれも最適化とは関係ありません。最適化は、解析ツリーが構築された後に行われるため、解析ツリーがどのように構築されたかを知ることはできません。これは、最も古くて愚かなコンパイラーから、最もスマートで最新のコンパイラーに変更することなく適用されます。インタプリタ言語(「コンパイル時間」と「実行時間」が一致する場合)でのみ、余分な括弧のペナルティが生じる可能性がありますが、そのような言語のほとんどは、少なくとも解析フェーズが1回だけ実行されるように構成されていると思います各ステートメント(実行のために事前に解析された形式を保存)。


s / oldes / oldest /。良い答え、+ 1。
デビッドコンラッド

23
全体的な警告:「質問はあまりうまくいっていません。」—「よく言う」とは「質問者がどの知識のギャップを埋めたいかを明確に示唆する」という意味に同意しません。本質的には、質問は「Xの最適化、AまたはBの選択、そしてその理由は何か?その下で何が起こるのか?」であり、少なくとも私にとっては、知識のギャップが非常に明確に示唆されています。あなたが正しく指摘し、非常にうまく対処している問題の欠陥は、それが不完全なメンタルモデルに基づいているということです。
ジョナスケルカー

の場合a = b + c * d;a = b + (c * d);[無害]冗長な括弧になります。それらがコードをより読みやすくするのに役立つなら、うまくいきます。a = (b + c) * d;非冗長な括弧になります-実際に結果の解析ツリーを変更し、異なる結果を与えます。行うことは完全に合法です(実際、必要です)が、暗黙のデフォルトのグループ化とは異なります。
フィルペリー

1
@OrangeDog true、その残念なベンのコメントは、VMがネイティブより速いと主張したい少数の人々を引き出しました。
gbjbaanb

1
@JonasKölker:私の冒頭の文は実際にタイトルで定式化された質問を指します:コンパイラが括弧を挿入するか削除するかについての質問に実際に答えることはできません。しかし、どの知識のギャップに対処する必要があるかは非常に明確です。
マークヴァンレーウェン

46

括弧は、コンパイラーではなく、ユーザーの利益のためだけにあります。コンパイラーは、ステートメントを表す正しいマシンコードを作成します。

参考までに、コンパイラは可能な限り完全に最適化するのに十分賢いです。あなたの例では、これはint a = 6;コンパイル時に変わります。


9
絶対に-好きなだけ括弧を付けて、コンパイラにあなたが望むものを理解するという大変な仕事をさせてください:)
gbjbaanb

23
@Serge True Programmingは、パフォーマンスよりもコードの読みやすさを重視しています。来年クラッシュをデバッグする必要があり、「最適化された」コードしか持っていないとき、あなたは自分自身を嫌います。
ラチェットフリーク

1
@ratchetfreak、あなたは正しいですが、私は私のコードをコメントする方法も知っています。int a = 6; // =(1 + 2)+ 3
セルジュ

20
私はそれが時間のコメントでは、微調整の年後まで保持していないことを知っている@Sergeとコードが同期しなくなりますし、あなたがで終わるint a = 8;// = 2*3 + 5
ラチェットフリーク

21
またはwww.thedailywtf.comから:int five = 7; //HR made us change this to six...
Mooing Duck

23

あなたが実際に尋ねた質問に対する答えはノーですが、あなたが尋ねたつもりだった質問に対する答えはイエスです。括弧を追加しても、コードが遅くなることはありません。

最適化について質問しましたが、括弧は最適化とは関係ありません。コンパイラは、生成されたコードのサイズまたは速度のいずれか(場合によっては両方)を改善する目的で、さまざまな最適化手法を適用します。たとえば、式A ^ 2(Aの2乗)を取り、それが速い場合はA x A(Aをそれ自体で乗算)に置き換えます。ここでの答えは「いいえ」です。コンパイラーは、括弧が存在するかどうかによって最適化フェーズで何も変わりません。

読みやすさを向上させると思われる場所で、不必要な括弧を式に追加した場合、コンパイラが同じコードを生成するかどうかを尋ねたつもりだったと思います。言い換えると、括弧を追加すると、コンパイラーは、何らかの形で貧弱なコードを生成するのではなく、括弧を再び取り出すのに十分なほど賢くなります。答えは常に「はい」です。

慎重に言いましょう。厳密に不要な(式の評価の意味や順序に影響を与えない)式に括弧を追加すると、コンパイラーはそれらを静かに破棄し、同じコードを生成します。

ただし、明らかに不要な括弧が実際に式の評価の順序を変更する特定の式が存在し、その場合、コンパイラは実際に書いたものを有効にするコードを生成します。これは意図したものとは異なる場合があります。以下に例を示します。これをしないでください!

short int a = 30001, b = 30002, c = 30003;
int d = -a + b + c;    // ok
int d = (-a + b) + c;  // ok, same code
int d = (-a + b + c);  // ok, same code
int d = ((((-a + b)) + c));  // ok, same code
int d = -a + (b + c);  // undefined behaviour, different code

必要に応じて括弧を追加しますが、それらが本当に不要であることを確認してください!

私は決してしません。本当の利益をもたらさないエラーのリスクがあります。


脚注:符号付き整数式が、表現できる範囲外の値(この場合は-32767〜+32767)に評価されると、符号なしの動作が発生します。これは複雑なトピックであり、この回答の範囲外です。


最後の行の未定義の動作は、符号付きショートの符号の後には15ビットしかないため、最大サイズは32767であるということですよね?その些細な例では、コンパイラはオーバーフローについて警告するべきですよね?どちらの場合も反例の場合は+1。それらが関数のパラメーターである場合、警告は表示されません。また、a本当に符号なしの-a + b場合a、負の値とb正の値の場合、計算を先導することも簡単にオーバーフローする可能性があります。
パトリックM

@PatrickM:編集を参照してください。未定義の動作とは、コンパイラが警告を発行するかどうかなど、コンパイラが好きなことを行えることを意味します。符号なし算術はUBを生成しませんが、次に高い2のべき乗を法として減少します。
david.pfx

(b+c)最後の行の式はその引数をintにプロモートします。そのため、コンパイラintが16ビットであると定義しない限り(古代または小さなマイクロコントローラーをターゲットにしているため)、最後の行は完全に正当です。
supercat

@supercat:そうは思いません。結果の一般的なタイプとタイプは、short intである必要があります。それがこれまでに尋ねられたものではない場合、おそらく質問を投稿したいですか?
david.pfx

@ david.pfx:C算術プロモーションのルールは非常に明確です。その型がすべての値を表現できない場合intint除き、昇格されるよりも小さいものはすべて昇格されunsigned intます。定義されたすべての動作がプロモーションが含まれている場合と同じ場合、コンパイラはプロモーションを省略できます。16ビット型がラッピング抽象代数リングとして動作するマシンでは、(a + b)+ cとa +(b + c)は同等です。場合はint、オーバーフロー上に捕捉されたことを16ビットのタイプだった、しかし、その後、例が存在することになるところの表現の1 ...
supercat

7

ブラケットは、演算子の優先順位を操作するためのものです。コンパイルすると、実行時にブラケットが不要になるため、ブラケットは存在しなくなります。コンパイルプロセスは、あなたと私が必要とするすべての括弧、スペース、その他の構文糖を削除し、すべての演算子を、コンピューターが実行するためのはるかに単純なものに変更します。

だから、あなたと私が見るかもしれない場所...

  • 「int a =((1 + 2)+ 3);」

...コンパイラは次のようなものを出力する場合があります。

  • Char [1] :: "a"
  • Int32 :: DeclareStackVariable()
  • Int32 :: 0x00000001
  • Int32 :: 0x00000002
  • Int32 :: Add()
  • Int32 :: 0x00000003
  • Int32 :: Add()
  • Int32 :: AssignToVariable()
  • void :: DiscardResult()

プログラムは、先頭から開始して各命令を順番に実行することにより実行されます。
演算子の優先順位は「先着順」になりました。
コンパイラーは元の構文をバラバラにしてた間、すべてうまく機能したため、すべてが強く型付けされています。

わかりました、それはあなたと私が扱うもののようなものではありませんが、それから私たちはそれを実行していません!


4
このようなリモートでも何かを生成する単一のC ++コンパイラはありません。通常、それらは実際のCPUコードを生成し、アセンブリもこれを認識しません。
MSalters

3
ここでの目的は、記述されたコードとコンパイルされた出力の構造の違いを示すことでした。ここでもほとんどの人は、実際のマシンコードまたはアセンブリを読み込むことができないだろう
DHall

@MSalters Nitpicking clangは、LLVM ISAを「そのようなもの」として扱うと「そのようなもの」を出力します(スタックベースではなくSSAです)。LLVMのJVMバックエンドを作成することが可能であり、JVM ISA(AFAIK)はスタックベースのclang-> llvm-> JVMが非常に似ていることを考えると。
マチェイピエチョトカ

LLVM ISAには、実行時文字列リテラル(最初の2つの命令のみ)を使用して名前スタック変数を定義する機能はないと思います。これは、実行時とコンパイル時を真剣に混合しています。この質問はまさにその混乱に関するものであるため、区別が重要です。
MSalters

6

浮動小数点かどうかによって異なります:

  • 浮動小数点演算では、加算は結合的ではないため、オプティマイザーは操作を並べ替えることができません(fastmathコンパイラースイッチを追加しない限り)。

  • 整数演算では、並べ替えることができます。

この例では、両方がまったく同じコードにコンパイルされるため、両方がまったく同じ時間に実行されます(加算は左から右に評価されます)。

ただし、javaとC#でさえ最適化でき、実行時に実行されます。


+1は、浮動小数点演算が連想的でないことを示したためです。
ドーバル

質問の例では、括弧はデフォルト(左)の結合性を変更しないため、この点は重要ではありません。
マークヴァンレーウェン

1
最後の文については、そうは思いません。java aとc#の両方で、コンパイラは最適化されたバイトコード/ ILを生成します。ランタイムは影響を受けません。
ステファノアルティ

ILはこの種の式では動作しません。命令はスタックから特定の数の値を取得し、特定の数の値(通常は0または1)をスタックに返します。この種のことをC#の実行時に最適化するということは無意味です。
ジョンハンナ

6

典型的なC ++コンパイラは、C ++自体ではなく、マシンコードに変換します。無駄な括弧を削除します。はい、それが完了するまでには、括弧がまったくないからです。マシンコードはそのようには機能しません。



1

いいえ、しかしはい、しかし多分、しかし多分他の方法、しかしいいえ。

既に指摘されているように(C、C ++、C#、Javaなど、左に連想される言語を想定)、式((1 + 2) + 3)はとまったく同じ1 + 2 + 3です。これらは、ソースコードに何かを記述するさまざまな方法であり、結果のマシンコードまたはバイトコードに影響を与えません。

いずれにせよ、結果は、たとえば2つのレジスタを追加してから3番目を追加する命令、またはスタックから2つの値を取得、追加、プッシュバック、それから別の値を取得して追加、または3つのレジスタを追加する命令になります単一の操作、または次のレベルで最も賢明なもの(マシンコードまたはバイトコード)に応じて3つの数値を合計する他の方法。バイトコードの場合、これと同様の再構築が行われる可能性があります。たとえば、これに相当するIL(スタックへの一連のロード、および結果を追加してプッシュバックするペアのポップ)マシンコードレベルでそのロジックを直接コピーすることにはなりませんが、問題のマシンにとってより賢明なものになります。

しかし、あなたの質問にはさらに何かがあります。

正常なC、C ++、Java、またはC#コンパイラの場合、指定した両方のステートメントの結果が次とまったく同じ結果になると予想されます。

int a = 6;

結果のコードがリテラルの計算に時間を浪費するのはなぜですか?プログラムの状態を変更して1 + 2 + 36、being の結果が停止することはありません。そのため、実行中のコードではこれが必要です。実際、それでもないかもしれません(その6で何をするかに応じて、すべてを捨てることができるかもしれません;そして、C#でさえ「大幅に最適化しないでください、ジッタはこれを最適化するからです」同等int a = 6または単に不要なものをすべて捨てる)。

しかし、これはあなたの質問の可能な拡張に私たちを導きます。以下を考慮してください。

int a = (b - 2) / 2;
/* or */
int a = (b / 2)--;

そして

int c;
if(d < 100)
  c = 0;
else
  c = d * 31;
/* or */
int c = d < 100 ? 0 : d * 32 - d
/* or */
int c = d < 100 && d * 32 - d;
/* or */
int c = (d < 100) * (d * 32 - d);

(この最後の2つの例は有効なC#ではありませんが、他のすべてはC#、C ++、およびJavaで有効です。)

ここでも、出力に関してまったく同じコードがあります。定数式ではないため、コンパイル時に計算されません。1つのフォームが別のフォームよりも高速である可能性があります。どちらが速いですか?それは、プロセッサと、おそらくかなりarbitrary意的な状態の違いに依存します(特に高速な場合は、それほど高速ではない可能性が高いため)。

そして、それらはあなたの質問と完全に無関係ではありません。それらはほとんどが概念的に何かが行われる順序の違いに関するものだからです。

それぞれに、一方が他方よりも高速であると疑う理由があります。単一のデクリメントには特殊な命令がある場合があるため、(b / 2)--実際にはの場合よりも高速になり(b - 2) / 2ます。d * 32おそらくそれよりも速くd << 5製造することで、d * 32 - dより速く製造できましたd * 31。最後の2つの違いは特に興味深いものです。1つはいくつかのケースでいくつかの処理をスキップすることを許可しますが、もう1つは分岐の予測ミスの可能性を回避します。

したがって、これにより2つの質問が残ります。2.コンパイラは、遅いものを速いものに変換しますか?

そして答えは1です。2.たぶん。

または、問題のプロセッサに依存するため、拡張するには依存します。確かに、一方のナイーブなマシンコードの同等物が他方のナイーブなマシンコードの同等物よりも速いプロセッサが存在していました。電子コンピューティングの歴史の中で、常に高速なものもありませんでした(特に、分岐予測ミス要素は、パイプライン化されていないCPUがより一般的であった多くの場合、関連していませんでした)。

そしておそらく、コンパイラー(およびジッター、スクリプトエンジン)が実行するさまざまな最適化があり、特定のケースでは必須になるものもありますが、通常、論理的に同等なコードの一部を見つけることができるでしょう最もナイーブなコンパイラーでさえ、まったく同じ結果と論理的に同等のコードの一部を持ち、最も洗練されたものでも、一方より他方のコードの方が速いコードを生成します(ポイントを証明するために完全に病理学的なものを記述する必要がある場合でも)。

非常に小さな最適化の懸念のように思えるかもしれませんが、

いいえ。ここで示したものよりも複雑な違いがあっても、最適化とはまったく関係のない絶対に小さな懸念のようです。どちらかといえば、読みにくいのは読み((1 + 2) + 3やすいよりも遅いかもしれないと疑うので、それは悲観的な問題です1 + 2 + 3

しかし、C#/ Java / ...よりもC ++を選択することは、最適化(IMHO)のすべてです。

それが本当にC#やJavaよりもC ++を選択することが「すべて」である場合、人々はStroustrupとISO / IEC 14882のコピーを焼き付け、C ++コンパイラのスペースを解放して、さらにMP3または何かの余地を残すべきだと思います。

これらの言語には、それぞれ異なる利点があります。

その1つは、C ++の方が一般的にメモリの使用が高速で軽量であることです。ええ、C#やJavaがより高速であり、アプリケーションのライフタイムでメモリをより適切に使用できる例があります。これらは関連する技術が向上するにつれて一般的になりつつありますが、C ++で記述された平均的なプログラムはこれらの2つの言語のいずれかで同等のものよりも高速で、より少ないメモリを使用する、より小さな実行可能ファイル。

これは最適化ではありません。

最適化は、「物事を速くする」ことを意味するために使用されることがあります。多くの場合、「最適化」について実際に話しているときは、実際に物事を速くすることについて話しているので、一方が他方の速記になっているので、私は自分でその言葉を誤用することを認めます。

「物事を速くする」という正しい言葉は最適化ではありません。ここでの正しい言葉は改善です。プログラムに変更を加えた場合、唯一の重要な違いは、プログラムがより高速になり、最適化されずに改善されることです。

最適化とは、特定の側面や特定のケースに関して改善を行うことです。一般的な例は次のとおりです。

  1. あるユースケースでは高速になりますが、別のユースケースでは遅くなります。
  2. 現在は高速ですが、より多くのメモリを使用します。
  3. メモリは軽くなりましたが、遅くなりました。
  4. 現在は高速ですが、保守が難しくなっています。
  5. メンテナンスは簡単になりましたが、遅くなりました。

このような場合は、たとえば次の場合に正当化されます。

  1. 高速なユースケースは、より一般的であるか、そもそも深刻な障害があります。
  2. プログラムは許容できないほど遅く、多くのRAMが解放されています。
  3. プログラムは非常に多くのRAMを使用したため、超高速処理を実行するよりもスワップに多くの時間を費やしたため、停止しました。
  4. プログラムは容認できないほど遅く、理解しにくいコードは十分に文書化されており、比較的安定しています。
  5. プログラムは依然として許容できるほど高速であり、より理解しやすいコードベースを維持する方が安価であり、他の改善をより容易に行うことができます。

しかし、そのような場合は他のシナリオでも正当化されません。品質の絶対的な絶対的尺度によってコードが改善されたわけではなく、特定の用途により適した特定の点で改善されました。最適化。

言語の選択はここで効果があります。速度、メモリの使用、読みやすさはすべてそれに影響される可能性がありますが、他のシステムとの互換性、ライブラリの可用性、ランタイムの可用性、特定のオペレーティングシステムでのランタイムの成熟度も影響を受ける可能性があります(私の罪のために、私はどういうわけかLinuxとAndroidを私のお気に入りのOSとして、C#を私のお気に入りの言語として使用することになり、Monoは素晴らしいですが、私はまだこの1つにかなり思いつきます)。

「C#/ Java / ...よりもC ++を選択することは最適化に関することです」と言うのは、C ++が本当に悪いと思う場合にのみ意味があります。C ++の方が優れていると思う場合、最後に必要なことは、そのような可能な限り小さなマイクロオプトについて心配することです。確かに、おそらくそれを放棄する方が良いでしょう。幸せなハッカーも最適化する品質です!

ただし、「C ++が大好きで、C ++で気に入っていることの1つが余分なサイクルを絞り出すことだ」と言いたければ、それは別の問題です。マイクロオプトは、再帰的な習慣になる可能性がある場合にのみ価値があります(つまり、自然にコーディングする傾向は、低速であるよりも頻繁に高速になります)。さもなければ、彼らは時期尚早な最適化でさえなく、事態を悪化させる早すぎる悲観的です。


0

括弧は、式を評価する順序をコンパイラに指示するためにあります。とにかく使用される順序を指定するため、それらは役に立たないことがあります(読みやすさを向上または悪化させることを除く)。順番が変わることもあります。に

int a = 1 + 2 + 3;

事実上、存在するすべての言語には、1 + 2を加算し、結果に3を加算して合計を評価するというルールがあります。

int a = 1 + (2 + 3);

括弧は別の順序を強制します。最初に2 + 3を追加し、次に1に結果を追加します。括弧の例は、とにかく生成されるのと同じ順序を生成します。この例では、演算の順序はわずかに異なりますが、整数加算の動作方法は同じです。に

int a = 10 - (5 - 4);

括弧は重要です。それらを省略すると、結果は9から1に変わります。

コンパイラーがどの操作をどの順序で実行するかを決定した後、括弧は完全に忘れられます。この時点でコンパイラが覚えているのは、どの操作をどの順序で実行するかだけです。そのため、実際にはコンパイラが最適化できるものは何もありません。括弧はなくなりました


practically every language in existence; APLを除く:(ここ)[tryapl.org]に(1-2)+3(2)、1-(2+3)(-4)、および1-2+3(-4)を入力してみてください。
tomsmeding

0

言われたことの多くに同意しますが、ここで重要なことは、操作の順序を強制するために括弧が存在するということです...これはコンパイラーが絶対にやっていることです。はい、それはマシンコードを生成します…しかし、それはポイントではなく、求められているものでもありません。

括弧は実際になくなりました。既に述べたように、それらはマシンコードの一部ではなく、数字であり、他のものではありません。アセンブリコードはマシンコードではなく、人間が読める形式であり、オペコードではなく名前で指示が含まれています。マシンは、オペコードと呼ばれるもの、つまりアセンブリ言語の数値表現を実行します。

Javaなどの言語は、それらを生成するマシン上で部分的にしかコンパイルされないため、中間の領域に分類されます。それらは、それらを実行するマシン上でマシン固有のコードにコンパイルされますが、この質問には違いはありません-括弧は最初のコンパイル後に消えます。


1
これが質問に答えるかどうかはわかりません。補助的な段落は、役立つというよりも混乱を招くものです。JavaコンパイラはC ++コンパイラとどのように関連していますか?
アダムザッカーマン

OPは、括弧がなくなったかどうかを尋ねました…私は、括弧がなくなったと言い、実行可能コードはオペコードを表す単なる数字であるとさらに説明しました。Javaは別の答えで育てられました。私はそれが問題にうまく答えると思うが…それは私の意見に過ぎない。返信いただきありがとうございます。
jinzai

3
括弧は「操作の順序」を強制しません。優先順位が変わります。だから、中にはa = f() + (g() + h());、コンパイラは自由に呼び出すことができfgおよびhその順序で(あるいはそれが喜ば任意の順序で)。
アロク

私はその声明にいくらか意見の相違があります…操作の順序を括弧で絶対に強制できます。
jinzai

0

コンパイラーは、言語に関係なく、すべてのインフィックス数学をポストフィックスに変換します。言い換えると、コンパイラが次のようなものを見たとき:

((a+b)+c)

これは次のように変換されます。

 a b + c +

これは、中置記法は読みやすいが、後置記法はコンピュータがジョブを実行するために必要な実際の手順に非常に近いためです(そして、すでに十分に開発されたアルゴリズムがあるため)。定義では、postfixは操作順序や括弧に関するすべての問題を排除します。これにより、実際にマシンコードを記述するときに物事が自然に簡単になります。

このテーマの詳細については、逆ポーランド記法に関するウィキペディアの記事をお勧めます。


5
これは、コンパイラーによる操作の変換方法に関する誤った仮定です。たとえば、ここでスタックマシンを想定しています。ベクトルプロセッサを使用している場合はどうなりますか?大量のレジスタを備えたマシンがあった場合はどうなりますか?
アーメドマスード
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.