なぜ後置インクリメントがあるのですか?


55

免責事項:プレフィックスとポストフィックスのインクリメントのセマンティクスを完全に知っています。したがって、それらがどのように機能するかを私に説明しないでください。

スタックオーバーフローに関する質問を読んで、プログラマがpostfix increment演算子に何度も何度も混乱することに気づかずにはいられません。これから、次の疑問が生じます:後置インクリメントがコード品質の面で本当の利点を提供するユースケースはありますか?

例で私の質問を明確にしましょう。以下は、非常に簡潔な実装ですstrcpy

while (*dst++ = *src++);

しかし、それは私の本の中で最も自己文書化されたコードではありません(そして、それは正気のコンパイラで2つの迷惑な警告を生成します)。それでは、次の選択肢の何が問題になっていますか?

while (*dst = *src)
{
    ++src;
    ++dst;
}

その後、条件内の混乱する割り当てを取り除き、完全に警告のないコードを取得できます。

while (*src != '\0')
{
    *dst = *src;
    ++src;
    ++dst;
}
*dst = '\0';

(はい、私は知っている、srcそしてdstこれらの代替ソリューションで異なる結末値を持つことになりますが、以来strcpy、すぐにループの後に返す、それはこの場合には重要ではありません。)

後置インクリメントの目的は、コードをできるだけ簡潔にすることだと思われます。私はこれが私たちがどのように努力すべきかを単に見逃しています。これがもともとパフォーマンスに関するものであった場合、それは今日でも関連していますか?


7
接尾辞のバリアントは最も一般的に使用されるものであるため、それに対してケースを作成する場合は、かなり強力なケースの方が良いでしょう。そして、あなたの最初の例は少しストローマンです。実際にstrcpyこの方法でメソッドをコーディングする人はほとんどいないと思われます(既に述べた理由のため)。
ロバートハーベイ

4
なぜどちらかを持っているのですか?
ジェームズマクネリス

31
プログラマを混乱させる可能性のある言語からすべてを削除した場合、あまり多くの機能はありません。何かがあなたにとって役に立たない、あるいはごくまれしか役に立たないという事実は、それが切り取られるべきであることを意味しません。関係ない場合は、使用しないでください。終わり。
コーディグレー

4
ええ、それでは絶対にできませんint c = 0; c++;
Biff MaGriff

4
@Cody:いくつかのことが混乱を招き、便利だからです。彼はポストインクリメント自体と混同せず、その有用性について混乱しています。役に立たなかったり、混乱したりしないものはないはずです。
GManNickG

回答:


23

かつてはパフォーマンスに何らかの影響がありましたが、本当の理由はあなたの意図をきれいに表現するためだと思います。本当の問題は、何かwhile (*d++=*s++);が意図を明確に表現しているかどうかです。IMOはそうですが、あなたが提供する代替案はあまり明確ではありませんが、それは(簡単に)何十年もかけて物事のやり方に慣れてきた結果かもしれません。K&RからCを学んだこと(当時、Cに関する他の書籍ほとんどなかったため)おそらく助けにもなります。

ある程度までは、古いコードでは簡潔さがはるかに重要視されていたのは事実です。個人的には、これは主に良いことだったと思います。数行のコードを理解することは通常かなり簡単です。難しいのは、大量のコードを理解することです。テストと研究により、画面上のすべてのコードを一度に合わせることがコードを理解する上での主要な要因であることが繰り返し示されています。画面が拡大しても、これは真実のままであるように思われるため、コードを(合理的に)簡潔に保つことは依然として重要です。

もちろん、船外に出ることも可能ですが、これはそうではないと思います。具体的には、1行のコードを理解するのが非常に困難または時間がかかる場合、具体的には、より少ないコード行を理解するのがより多くの行を理解するよりも多くの労力を費やす場合、それは行き過ぎだと思います。これはLispとAPLで頻繁に発生しますが、(少なくとも私には)ここではそうではないようです。

私はコンパイラの警告についてあまり心配していません。多くのコンパイラがかなり定期的にまったくばかげた警告を発するのは私の経験です。私は確かに人々は自分のコード(およびそれが生成する可能性のある警告)を理解すべきだと思いますが、一部のコンパイラで警告を引き起こすまともなコードは必ずしも間違っているわけではありません。確かに、初心者は安全に無視できるものを常に知っているわけではありませんが、私たちは永遠に初心者のままではなく、私たちのようにコーディングする必要もありません。


12
+1は、一部の人にとっては簡潔なバージョンのが読みやすいと指摘しているためです。:-)

1
コンパイラの警告のいくつかが気に入らない場合(そして、真剣に、私は入力するつもりだったif(x = y)、私は誓います!)、コンパイラの警告オプションを調整して、誤って見逃さないようにしてくださいあなた好きな警告。

4
@Brooksモーゼス:私はたくさんのことについてあまり熱狂。コンパイラの欠点を補うためにコードを汚染するのは好きではありません。
ジェリーコフィン

1
@ Jerry、@ Chris:この注釈は、警告は通常良いことだと思うが、何か異常なことをしている特定の行に適用したくない場合に使用します。警告を決して必要とせず、それを欠点と見なす場合は、を使用しますが-Wno-X、例外を1つだけ行い、警告を他のすべての場所に適用したい場合は、Chrisが言及するような難解なフープジャンプを使用するよりもはるかにわかりやすいです。

1
@Brooks:二重括弧と(void)x未使用警告を沈黙させることは、それ以外の場合は有用な警告を沈黙させることでよく知られているイディオムです。注釈は少し似ていますがpragmas、最高の移植性はありません:/
Matthieu M.

32

それは、エラー、ハードウェアのものでした


おもしろいと思います。Postfix incrementはおそらくいくつかの完全に正当な理由で存在しますが、Cの多くのものと同様に、その人気は言語の起源に由来します。

Cはさまざまな初期のパワー不足のマシンで開発されましたが、CとUnixは最初にPDP-11のメモリ管理モデルで比較的大ヒットしました。これらは当時の比較的重要なコンピューターであり、Unixは-11で利用可能な他の7つのぎこちないオペレーティングシステムよりもはるかに優れていました。

そして、PDP-11では、

*--p

そして

*p++

... アドレス指定モードとしてハードウェアに実装されました。(また*p、他の組み合わせはありません。)これらの初期のマシンでは、すべて0.001 GHz未満で、1命令または2ループを保存することは、ほとんど1秒または1分またはアウトでした。 -ランチの違い。これは、具体的にはポストインクリメントに正確に対応しているわけではありませんが、ポインターポストインクリメントによるループは、当時のインデックス作成よりもはるかに優れている可能性があります。

結果として、設計パターンは、Cの精神マクロになったCのイディオムのためです。

これは、{...の直後に変数を宣言するようなものです。C89が現在の要件であったためではなく、コードパターンになりました。

更新:明らかに、主な理由*p++は言語にあります。それは、それが頻繁にやりたいことだからです。コードパターンの人気は、PDP-11が到着する少し前に設計された既存の言語パターンと一致する人気のあるハードウェアによって強化されました。

このごろ、それはあなたが使用するか、またはあなたがインデックスを使用している場合、パターン違いはありませんし、とにかく高いレベルで我々は通常のプログラムが、それはそれらの0.001GHzのマシン上で多くのことを大事、と以外のものを使用している必要があります*--x*x++あなたを意味しているだろうPDP-11を「入手」しなかったので、人々があなたに近づいてきて「それを知っていましたか...」と言うかもしれません:-) :-)


25
ウィキペディアは言う:「(偽)民俗神話はPDP-11の命令セットアーキテクチャは、Cプログラミング言語の慣用的な使用に影響を与えたということであるPDP-11のインクリメントとデクリメントのアドレッシングモードが対応。−−ii++C.もし中の構築ijどちらもレジスタ変数であり*(−−i) = *(j++)、単一の機械語命令にコンパイルできるような式です。[...]デニスリッチーはこの民俗神話と明確に矛盾しています。[C言語の開発 ] "
-fredoverflow

3
Huh(FredOverflowのコメントで提供されるリンクから):「人々はしばしば、CとUnixが最初に普及したDEC PDP-11によって提供される自動インクリメントおよび自動デクリメントアドレスモードを使用するために作成されたと推測します。 Bの開発時にはPDP-11がなかったため不可能でしたが、PDP-7にはいくつかの「自動インクリメント」メモリセルがあり、それらを介した間接メモリ参照によってセルがインクリメントされるという特性がありました。そのような演算子をトンプソンに提案しました;それらを接頭辞と接尾辞の両方にする一般化は彼自身のものでした。」

3
DMRは、PDP-11がCに追加された理由ではないと言っただけです。私もそう言いました。私が言ったことは、それがPDP-11で有利に使用され、その正確な理由で人気のあるデザインパターンになったことです。ウィキペディアが矛盾している場合、それらは単に間違っていますが、私はそのようには読みません。そのWページでは不適切に表現されています。
DigitalRoss

3
@FredOverflow。あなたは、「民俗神話」が何であるかを正確に誤解したと思います。神話は、Cがこれらの11のopを活用するように設計されていることであり、それらが存在しないことや、誰もが11にマッピングされていることを知っていたためにコードパターンが普及しなかったことではありません。明確でない場合:PDP-11は、アドレス指定モードとして、ほとんどすべての命令に対して実際にこれらの2つのモードをハードウェアに実装しており、実際にCが実行された最初の重要なマシンでした。
DigitalRoss

2
「0.001GHzのマシンでは、それは非常に重要だったに違いない」私は日付がついているのでそれを言うのは嫌いですが、CPUのMotorolaおよびIntelのマニュアルで、命令のサイズとそれらにかかったサイクル数を調べました。プリントスプーラーにもう1つの機能を追加できるように追加された最小のコードを見つけて、数サイクルを節約することは、シリアルポートが新しく考案されたMIDIのようなプロトコルに対応できることを意味しました。Cは、特にコンパイラが改善されたため、いくつかのちょっとしたピッキングを緩和しましたが、それでもコードを記述する最も効率的な方法を知ることが重要でした。
ティンマン

14

接頭辞と接尾辞--++演算子は、ケン・トンプソンによってB言語(Cの前身)に導入された-となし、それらはPDP-11、一度に存在しなかったに触発されていませんでした。

デニス・リッチーの「C言語の開発」から引用:

トンプソンが考案によるさらなるステップを行ってきました++し、--インクリメントまたはデクリメントする演算子。接頭辞または接尾辞の位置によって、オペランドの値を記録する前または後に変更が発生するかどうかが決まります。それらはBの初期バージョンではありませんでしたが、途中で登場しました。人々は、CとUnixが最初に普及したDEC PDP-11が提供する自動インクリメントおよび自動デクリメントアドレスモードを使用するために作成されたとしばしば推測します。Bが開発されたときにPDP-11がなかったため、これは歴史的に不可能です。ただし、PDP-7にはいくつかの「自動インクリメント」メモリセルがあり、それらを介した間接メモリ参照によってセルがインクリメントされるという特性がありました。この機能は、おそらくそのような演算子をトンプソンに提案しました。それらを接頭辞と接尾辞の両方にする一般化は彼自身のものでした。確かに、++xはのものより小さかったですx=x+1


8
いいえ、私はケン・トンプソンとは関係ありません。
キーストンプソン

この非常に興味深い参照をありがとう!これは、技術的な最適化ではなくコンパクトさの目標を示唆した元のK&Rからの引用を裏付けています。ただし、これらの演算子が既にBに存在することは知りませんでした
クリストフ

@BenPen私の知る限り、両方の質問がそれぞれのサイトに適合する限り、異なる SEサイトに重複がある場合、質問を閉じるポリシーはありません。
オーダス

おもしろい...プログラマーは確かに質問に近いものではありません。
ベンペン

@BenPen:Stack Overflowの質問に対する受け入れられた答えは、私がしたのと同じソースをすでに引用しています(ただし、それほど多くはありません)。
キーストンプソン

6

postincrement演算子が存在する明白な理由は、あちこちに、またはあちこちに式を記述したり、わかりにくい副作用のある些細な文を複数の文に無駄に分け(++x,x-1)たりする必要がないようにするため(x+=1,x-1)です。ここではいくつかの例を示します。

while (*s) x+=*s++-'0';

if (x) *s++='.';

文字列を順方向に読み書きするときは常に、プリインクリメントではなくポストインクリメントが自然な操作です。事前インクリメントの実際の使用にはほとんど遭遇しませんでしたが、事前減少の主な用途は、数字を文字列に変換することだけです(人間の書記システムは数字を逆方向に書き込むため)。

編集:実際にはそれほどいものではないでしょう。代わりに(++x,x-1)、もちろん++x-1またはを使用できます(x+=1)-1。それでもx++読みやすいです。


2つのシーケンスポイント間で変数を変更して読み取るため、これらの式に注意する必要があります。これらの式が評価される順序は保証されません。

@スノーマン:どれ?私の答えにはそのような問題は見当たりません。
R .. 14

あなたのようなものがあれば(++x,x-1)(?関数呼び出し、多分)

@スノーマン:それは関数呼び出しではありません。これは、優先順位を制御するために括弧で囲まれたシーケンスポイントであるCコンマ演算子です。結果はx++ほとんどの場合と同じです(1つの例外:xランクがより低い符号なしの型でint、初期x値はその型の最大値です)。
R .. 14

ああ、トリッキーなコンマ演算子...コンマがコンマでない場合。カンマ演算子の括弧と希少性は私を追い払った。気にしないで!

4

PDP-11は、命令セットでポストインクリメントおよびプレデクリメント操作を提供しました。今、これらは指示ではありませんでした。これらは、レジスターの値がインクリメントされる前またはデクリメントされた後に、レジスターの値が必要であることを指定できる命令修飾子です。

機械語でのワードコピー手順は次のとおりです。

movw (r1)++,(r2)++

これにより、ある場所から別の場所に単語が移動され、レジスタによってポイントされ、レジスタは再びそれを実行する準備が整います。

CがPDP-11上に構築されたため、多くの有用な概念がCに取り入れられました。Cは、アセンブラー言語の有用な代替となることを目的としていました。対称性のために、プリインクリメントとポストデクリメントが追加されました。


それは素晴らしい修飾子です... PDP-11アセンブリを学びたいと思うようになります。ところで、「対称性について」のリファレンスはありますか?それは理にかなっていますが、これは歴史であり、意味をなさないことが重要なこともありました。笑
ベンペン

2
アドレス指定モードとも呼ばれます。PDP-11は、スタック操作のためにうまく組み合わせられたポストインクリメントとプレデクリメントを提供しました。
エリックエイド

1
PDP-8はポストインクリメントメモリインダイレクションを提供しましたが、対応するプリデクリメント操作はありませんでした。磁気コアメモリでは、メモリワードを読み取るとデータが消去されたため(!)、プロセッサはライトバック機能を使用して、読み取ったワードを復元しました。書き戻しの前に増分を適用することはかなり自然に起こり、より効率的なブロック移動が可能になりました。
エリックエイド

3
申し訳ありませんが、PDP-11はこれらのオペレーターを刺激しませんでした。ケン・トンプソンが彼らを発明したとき、それはまだ存在していませんでした。私の答えをご覧ください。
キーストンプソン

3

私はそれが本当に本当に必要だったことを疑います。私の知る限り、ほとんどのプラットフォームで、ループでプリインクリメントを使用するよりもコンパクトにコンパイルされることはありません。作成された時点では、コードの明快さよりもコードの簡潔さが重要でした。

つまり、コードの明瞭さは重要ではない(または重要ではなかった)とは言いませんが、低ボーモデム(ボーで測定するものはすべて遅い)で入力するときは、すべてのキーストロークでそれを行わなければなりませんでしたメインフレームをチェックし、パリティチェックとして1ビットを使用して一度に1バイトをエコーバックするので、あまり入力する必要はありません。

これは、&や|のようなものです。==より低い優先順位を持つ演算子

最高の意図で設計されました(&&および||の非短絡バージョン)が、現在ではプログラマを毎日混乱させており、おそらく変わらないでしょう。

とにかく、これはあなたの質問に対する良い答えはないと思うという点での答えにすぎませんが、おそらく私よりも第一人者のコーダーによって間違っていることが証明されるでしょう。

-編集-

私は両方が非常に有用であること気付くべきです、私はただそれが誰かがそれを好きかどうかにかかわらず変わらないことを指摘しています。


これは、リモートでも事実ではありません。明快さよりも「コードの簡潔さ」が重要になることはありませんでした。
コーディグレー

まあ、私はそれがはるかに焦点のポイントだったと主張します。ただし、コードの明快さよりも重要なことは、ほとんどの人にとっては間違いありません。

6
まあ、「簡潔」の定義は「滑らかにエレガント:洗練された」または「少数の単語を使用する:過剰な表現がない」であり、どちらも一般にコードを書くときに良いことです。「Terse」は、多くの人が考えるように「不明瞭で読みにくく、難読化された」という意味ではありません。
ジェームズマクネリス

@James:あなたは正しいですが、ほとんどの人は、プログラミングコンテキストで使用する場合の「簡潔」の2番目の定義を意味していると思います。その定義の下では、特定のコードの簡潔さと表現力の間にトレードオフがある状況を想像することは確かに簡単です。明らかに、コードを書くときの正しいことは、話すときと同じです。必要なだけ短く、短くはしません。表現力より簡潔さを重視すると、コードが難読化されたコードになる可能性がありますが、そうすべきではありません。
コーディグレー

1
40年巻き戻して、約1000文字しか保持できないドラムにプログラムを収めなければならなかったと想像するか、コンパイラを書くと4,000バイトのRAMでしか動作しません。簡潔さと明瞭さが問題にならないこともありました。簡潔である必要がありました。この惑星には、簡潔でないプログラムを保存、実行、またはコンパイルできるコンピューターは存在しませんでした。
番号

3

ポインターを処理するとき、ポストフィックスアペレーターが好きです(逆参照しているかどうかに関係なく)

p++

同等のものよりも「次のスポットに移動する」と自然に読みます

p += 1

初心者がsizeof(* p)!= 1のポインターで使用されているのを初めて見たときに、混乱させる必要があります。

混乱の問題を正しく述べているのですか?接尾辞++演算子が逆参照*演算子よりも優先順位が高いという事実を初心者を混乱させるものではありません。

*p++

として解析

*(p++)

ではなく

(*p)++

一部の人が期待するように?

(あなたはそれが悪いと思いますか?C宣言読むを参照してください。)


2

優れたプログラミングの微妙な要素には、ローカリゼーションミニマリズムがあります。

  • 変数を最小限の使用範囲に入れる
  • 書き込みアクセスが不要なときにconstを使用する

同じ精神では、x++現在の値を基準ローカライズの方法として見ることができるxかどうか、その値を有する完成し、有効(次へ移動したい-少なくとも今のところは-あなたがしたことを示すすぐにしばらく時間がxありますintか、ポインタまたはイテレータ)。少し想像すると、これは、古い値/位置がx不要になった直後に「範囲外」になり、新しい値に移動することに匹敵できます。 その遷移が可能な正確なポイントは、接尾辞によって強調表示されます++。これがおそらく「古い」ものの最終的な使用であるという意味はx、アルゴリズムに対する貴重な洞察となり、プログラマが周囲のコードをスキャンする際に役立ちます。もちろん、接尾辞++ プログラマーは新しい値の使用に目を光らせます。これは、新しい値が実際に必要になるタイミングに応じて良い場合もあれば悪い場合もあります。状況に役立ちます。

多くの初心者は機能に戸惑うかもしれませんが、それを長期的な利益とバランスさせる価値があります。初心者は初心者に長く留まらない。


2

うわー、多くの答えはあまり意味がありません(私がとても大胆かもしれないなら)、そして私が明白なことを指摘しているなら許してください-特にセマンティクスを指摘しないためにあなたのコメントに照らして(Stroustrupの観点からは、私は推測します)はまだ投稿されていないようです!:)

Postfix x++は、式で「上向き」に渡される一時変数を生成しますxが、変数はその後インクリメントされます。

プレフィックス++xは一時オブジェクトを生成しませんが、「x」をインクリメントし、結果を式に渡します。

値は、演算子を知っている人にとっては便利です。

例えば:

int x = 1;
foo(x++); // == foo(1)
// x == 2: true

x = 1;
foo(++x); // == foo(2)
// x == 2: true

もちろん、この例の結果は、他の同等の(そしておそらくより斜めでない)コードから生成できます。

では、なぜ後置演算子があるのでしょうか?明らかに混乱が生じているにもかかわらず、それは永続的なイディオムだからだと思います。かつて価値があると認識されていたのはレガシーコンストラクトですが、認識された価値がパフォーマンスにとって便利であるかどうかはわかりません。その利便性は失われていませんが、読みやすさの評価が高まり、オペレーターの目的が問われていると思います。

後置演算子はもう必要ですか?そうではない。その結果は混乱を招き、理解しやすさの障壁となります。一部の優れたコーダーは、しばしば「PERL風」の独特の美しさがある場所で、すぐにそれがどこで役立つかを確実に知るでしょう。その美しさの代償は読みやすさです。

明示的なコードには、簡潔さ、読みやすさ、理解可能性、保守性よりも利点があることに同意します。優れたデザイナーとマネージャーはそれを望んでいます。

ただし、一部のプログラマーは、純粋に美しい単純化(postfix演算子など)を使用してコードを作成することを奨励する特定の美しさがあります。それにはある種の何かがありますが、それは、たとえそれがより望ましくないとしても、それをより美しくするだけです。

言い換えれば、while (*dst++ = *src++);単により美しい解決策であると感じる人もいます。それは、キャンバスへのブラシであるかのように、そのシンプルさで息を引き取るものです。あなたが言語を理解することを強制されて美しさを認めることは、その素晴らしさを増すだけです。


3
+1「一部の人々はwhile(* dst ++ = * src ++);単により美しい解決策を見つける」絶対に。アセンブリ言語を理解していない人は、Cがどれほどエレガントであるかを本当に理解していませんが、その特定のコードステートメントは輝かしい星です。
ティンマン

「後置演算子はもう必要ですか?おそらく必要ありません。」-ここでチューリングマシンのことを考えざるを得ません。自動変数、forステートメント、さらにはwhile(必要なのに)必要なことはgotoありますか?

@Bernd:ハ!閉鎖を想像してください!閉鎖!スキャンダラス!笑
ブライアンM.ハント

閉鎖!なんてばかげている。テープをください!真剣に、私が意味したのは、機能が主要な決定基準であるべきだとは思わないということです(たとえば、Lispを設計したい場合を除きます)。IME私はほとんどの場合、接尾辞演算子を使用します(いや、必要)。

2

Kernighan&Ritchieのオリジナルの正当化を見てみましょう(オリジナルのK&Rページ42および43):

珍しい点は、++および-が接頭辞または接尾辞として使用できることです。(...)値が不要なコンテキストでは(..)好みに応じて接頭辞または接尾辞を選択します。しかし、htereは、どちらか一方が特に必要とされる状況です。

テキストは、インデックス内で増分を使用するいくつかの例を続けており、「よりコンパクトな」コードます。したがって、これらの演算子の背後にある理由は、よりコンパクトなコードの利便性です。

与えられた3つの例(squeeze()getline()およびstrcat())はインデックスを使用する式内で後置記号のみを使用します。著者は、埋め込みインクリメントを使用しないより長いバージョンとコードを比較します。これは、コンパクトさが焦点であることを確認します。

K&Rのハイライト(102ページ)、これらの演算子とポインターの逆参照(例: *--pおよびなど*p--)と。これ以上の例はありませんが、繰り返しますが、利点はコンパクトであることを明確にしています。

接頭辞は、たとえば、インデックスまたは末尾から始まるポインタを減らすときに非常によく使用されます。

 int i=0; 
 while (i<10) 
     doit (i++);  // calls function for 0 to 9  
                  // i is 10 at this stage
 while (--i >=0) 
     doit (i);    // calls function from 9 to 0 

1

私は、という前提に反対するつもりです++pp++、何とか読みにくいかは不明です。1つは「pを増分してからpを読み取る」ことを意味し、もう1つは「pを読み取ってからpを増分する」ことを意味します。どちらの場合も、コード内の演算子はコードの説明内の正確な場所にあるため、++意味がわかっている場合は、結果のコードの意味がわかります。

任意の構文を使用して難読化されたコードを作成できますが、ここではp++/ ++p本質的に難読化されるケースは見当たりません。


1

これは、電気技術者のPOVからのものです。

後入れ先出し(LIFO)スタックを維持する目的で、組み込みのポストインクリメント演算子とプリデクリメント演算子を備えたプロセッサが多数あります。

次のようになります。

 float stack[4096];
 int stack_pointer = 0;

 ... 

 #define push_stack(arg) stack[stack_pointer++] = arg;
 #define pop_stack(arg) arg = stack[--stack_pointer];

 ...

私は知らないが、それがプレフィックスとポストフィックスの両方のインクリメント演算子を見ることを期待する理由です。


1

コンパクトさについてのChristopheのポイントを強調するために、V6のいくつかのコードを示したいと思います。これは、http//minnie.tuhs.org/cgi-bin/utree.pl?file = V6 / usr / source / c / c00.cにある元のCコンパイラの一部です。

/*
 * Look up the identifier in symbuf in the symbol table.
 * If it hashes to the same spot as a keyword, try the keyword table
 * first.  An initial "." is ignored in the hash.
 * Return is a ptr to the symbol table entry.
 */
lookup()
{
    int ihash;
    register struct hshtab *rp;
    register char *sp, *np;

    ihash = 0;
    sp = symbuf;
    if (*sp=='.')
        sp++;
    while (sp<symbuf+ncps)
        ihash =+ *sp++;
    rp = &hshtab[ihash%hshsiz];
    if (rp->hflag&FKEYW)
        if (findkw())
            return(KEYW);
    while (*(np = rp->name)) {
        for (sp=symbuf; sp<symbuf+ncps;)
            if (*np++ != *sp++)
                goto no;
        csym = rp;
        return(NAME);
    no:
        if (++rp >= &hshtab[hshsiz])
            rp = hshtab;
    }
    if(++hshused >= hshsiz) {
        error("Symbol table overflow");
        exit(1);
    }
    rp->hclass = 0;
    rp->htype = 0;
    rp->hoffset = 0;
    rp->dimp = 0;
    rp->hflag =| xdflg;
    sp = symbuf;
    for (np=rp->name; sp<symbuf+ncps;)
        *np++ = *sp++;
    csym = rp;
    return(NAME);
}

これは、良いスタイルの教育としてsamizdatで受け継がれたコードですが、今日のようにそれを書くことは決してありません。副作用ifwhile条件がいくつパックされているか、そして逆に、両方のforループがインクリメント式を持たないのは、ループ本体で行われるためです。どうしても必要な場合にのみブロックがブレースで囲まれる方法を見てください。

これの一部は確かに、今日コンパイラに期待される種類の手動のマイクロ最適化でしたが、コンピューターへのインターフェイス全体が単一の80x25ガラスttyである場合、コードが欲しいという仮説も提示しますできるだけ密に配置して、同時に多くを表示できるようにします。

(すべてにグローバルバッファを使用することは、おそらくアセンブリ言語に歯を切ることからの二日酔いです。)


注意して扱う:「ihash = + * sp ++;」ある時点で、Cには+ =だけでなく= +演算子もありました。今日、= +は代入演算子であり、その後に何もしない単項プラスが続くため、「ihash = * sp; sp + = 1;」と同等です。
gnasher729

@ gnasher729はい、あとでがありますがrp->hflag =| xdflg、これは現代のコンパイラでは構文エラーになると思います。「ある時点で」は、まさにV6です。+=フォームへの切り替えはV7で行われました。(V6コンパイラは、このコードを正しく読んでいる場合にのみ受け入れ=+ました。同じファイルのsymbol()を参照してください。)
zwol

-1

おそらく化粧品についても考える必要があります。

while( i++ )

おそらく「より良く見える」よりも

while( i+=1 )

何らかの理由で、ポストインクリメント演算子は非常に魅力的に見えます。短く、甘く、すべてを1ずつ増やします。プレフィックスと比較します。

while( ++i )

「後ろを向いている」ではありませんか?数学で最初にプラス記号が表示されることはありません。ただし、誰かが何かポジティブなもの(+0またはそのようなもの)を指定することに愚かである場合を除きます。私たちはOBJECT + SOMETHINGを見ることに慣れています

グレーディングと関係がありますか?A、A +、A ++?私は、Pythonの人々operator++が彼らの言語から削除したことを最初はかなりcheeseりました!


1
あなたの例は同じことをしません。 i++元の値を返すi、とi+=1++iの元の値を返すiあなたがコントロールフローの戻り値を使用しているのでプラス1を、これが問題になります。
デビッドソーンリー

はい、あなたが正しい。しかし、私はここで読みやすさだけを見ています。
ボボボボ

-2

9から0までの逆算:

for (unsigned int i=10; i-- > 0;)  {
    cout << i << " ";
}

または同等のwhileループ。


1
どうfor (unsigned i = 9; i <= 9; --i)
fredoverflow
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.