C ++の「->」演算子とは何ですか?


8925

読んだ後に隠された機能とCのダークコーナー++ / STLのをcomp.lang.c++.moderated、私は完全に次のコードがコンパイルおよびVisual Studio 2008とG ++ 4.4の両方で働いていたことに驚きました。

コードは次のとおりです。

#include <stdio.h>
int main()
{
    int x = 10;
    while (x --> 0) // x goes to 0
    {
        printf("%d ", x);
    }
}

出力:

9 8 7 6 5 4 3 2 1 0

GCCでも動作するので、これはCだと思います。これは規格のどこに定義されており、どこから来たのですか?


503
または、適切な間隔でも...変数との間に、++または--以前にスペースを見たことがないと思います...
Matthew Scharley、2009年

1154
この「移動先」演算子は元に戻すことができます(0 <-x)。また、「runs to」演算子(0 <---- x)もあります。Geez、これまで聞いた中で最もおもしろいのは、c ++構文=)+1の質問です。
SadSido、2009年

233
おかしなことに、解釈は非常に間違っていますが、コードが正しく行うことを説明しています。:)
ノルドリン2009

811
新しい構文の可能性を想像してください:#define upto ++<#define downto -->。悪を感じている場合は#define for while(、そして#define do ) {(そして#define done ;})書いて、for x downto 0 do printf("%d\n", x) doneああ、人間性を書くことができます...
Chris Lutz

98
まったく新しい表現力豊かなコーディング方法の可能性を開きます。コンパイラの警告をいくつか犠牲にするだけの価値があります。bool CheckNegative(int x){return x <0?真/偽 ); }
ttt

回答:


8603

-->は演算子ではありません。それは実際には二つの別々の事業者である、-->

条件付きのコードはを減らしxx元の(減らされていない)値を返します。次に0>演算子を使用して元の値を比較します。

よりよく理解するために、ステートメントは次のように書くことができます。

while( (x--) > 0 )

262
その場合も、そのコンテキストでは、ある種の範囲演算子のように見えます。
Charles Salvia、

109
xがポストデクリメントされ、次に0と比較されると言うことは、xが0と比較された後でデクリメントされると言うことと同じです
Charles Salvia

8
同じではないと思います。「それから」という言葉は、順序があることを意味すると思います(ポストデクリメント後、xの値は1つ少なくなります)。「xをデクリメントしてから、古い値と0を比較しています...」と言って、それをより明確にすることができると思います。しかし、これはとにかくつまらない。誰もが何を意味するか知っています。
ヨハネスシャウブ-litb 2010年

38
Javaでもコンパイルされます:)
Steven Devijver

44
その名前、@ Jayは悪いプログラミングスタイルです:-)これは、最初に質問されたという事実によって証明されます。オペレータを関係のないものではなく、テキストでオペレータが操作しているものにバインドする方がはるかに理にかなっているため、while (x-- > 0)より適切です。また、何が起こっているのか(少なくとも固定フォントエディターで)がより明確になり、この回答の括弧が不要になることを意味します。
paxdiablo 2016

3131

または完全に異なる何かのために...にxスライドし0ます。

while (x --\
            \
             \
              \
               > 0)
     printf("%d ", x);

それほど数学ではありませんが...すべての絵は千の言葉を描きます...


216
@mafutrct-覚えているように、Cでは\は改行がないかのように次の行を追加するだけです。ここの\ sは基本的に何もしません。
Hogan

2
@mafu '\'文字は、現在の行が次の行に続くことをコンパイラーに通知するため、コンパイラーは両方の行をマージして1つとしてコンパイルする必要があります。'while(x-> 0)'は、xが-1になるまで実行されます。しかし、彼がインデントを作成する方法は、xがゼロにスライドしているように見せます。「xが0にスライドする間...」
Felype

16
IIRC、K&R Cでは、デクリメント演算子の「-」の間に空白を使用できました。その場合、途中にバックスラッシュがあり、さらに冷たく見えます。:)
Jules

10
@ArnavBorborahこれは古い表現の意味でwhy waste words when a picture does a better job、このコンテキストでジョークとして使用されます。(実際には2つのキーワードwhileとがありますprintf
同期されていない

88
ああ、あいまいなスライドオペレーター。忘れられる訳がない!
Demonkoryu 2017年

2377

これは非常に複雑な演算子であるため、ISO / IEC JTC1(合同技術委員会1)でさえ、その説明をC ++標準の2つの異なる部分に配置しています。

冗談はさておき、これらは2つの異なる演算子で--あり>、それぞれC ++ 03標準の§5.2.6/ 2および§5.9で説明されています。


1277

それは同等です

while (x-- > 0)

x--(デクリメント後)はそれと同等x = x-1であり、コードは次のように変換されます。

while(x > 0) {
    x = x-1;
    // logic
}
x--;   // The post decrement done when x <= 0

15
これは正しくありません。2番目のケースでは、ループ本体内のxの値が異なります。例の割り当てステートメントは、同等になるようにロジックの上にある必要があります。Postfix-1を減算しますが、比較は減算の値で行われます。
公証人2018年

4
@uliwitnessこれらは完全に同等です。プレフィックスが使用されている場合は誤りです0 >-- x。この場合x、ロジックの前にデクリメントされます。postfixでは、ロジックはデクリメントの前に実行されるため、両方のサンプルは同等です。それらを自由に書いてConsoleテストしてください。
Candleshark、

12
それらはまだ同等ではありません。最初のループの後、xは-1(または符号なしの場合はオーバーフロー)、2番目のループの後は0になります(xが負でない値で始まると仮定すると、どちらのループもxを変更したり、中断したりしません...)
シーザー

1
while(x=x-1,x+1 > 0)同等です。
SS

2
@Shebham、これが反例です:xが0で始まる場合、元のループでは-1で終了し、バージョンは0のままです。したがって、それらは同等ではありません。
エリオット

1209

x 反対方向にさらに速くゼロに行くことができます:

int x = 10;

while( 0 <---- x )
{
   printf("%d ", x);
}

8 6 4 2

矢印でスピードをコントロールできます!

int x = 100;

while( 0 <-------------------- x )
{
   printf("%d ", x);
}

90 80 70 60 50 40 30 20 10

;)


6
どのオペレーティングシステム、このタイプの出力が生成されたのか、ubuntu 12.04を使用していますが、エラーメッセージが表示されました
Bhuvanesh

74
これは明らかなはずですが、C ++を初めて使用するすべての人にとって、これを読んでください。複数の増分/減分が必要な場合は、拡張割り当てを使用してください。
Blimeo 2015年

263
「レーザー」でゼロ。while(0>-------------------- x)...同じ出力。
Samuel Danielson 2016年

4
@phordコンパイルできませんか?-> coliru.stacked-crooked.com/a/5aa89a65e3a86c98
doc

18
@doc C ++ではコンパイルされますが、Cではコンパイルされません。
2016年

548

それは

#include <stdio.h>
int main(void){
     int x = 10;

     while( x-- > 0 ){ // x goes to 0

       printf("%d ", x);
     }

     return 0;
}

スペースだけで、物事がおかしくなり、--減り、>比較されます。


431

の使用に-->は歴史的な関連性があります。x86アーキテクチャでのインクリメントよりも、デクリメントの方が(そして場合によってはそれでも)高速でした。を使用-->することxは、に行くことを示唆しており0、数学的な背景を持つ人々にアピールします。


479
正確ではない。デクリメントとインクリメントには同じ時間がかかります。これの利点は、ゼロとの比較が変数との比較に比べて非常に速いことです。これは、x86だけでなく、多くのアーキテクチャに当てはまります。JZ命令のあるもの(ゼロの場合はジャンプ)。周りを突っ込むと、比較のサイクルを節約するために逆方向に書かれた「for」ループがたくさん見つかります。これはx86では特に高速で、変数をデクリメントする動作によりゼロフラグが適切に設定されるため、変数を明示的に比較する必要なく分岐できます。
burito 2009

25
まあ、ゼロに向けてデクリメントすることは、ループの反復ごとに0と比較するだけでよいことを意味し、nに向かって反復することは、各反復ごとにnと比較することを意味します。前者の方が簡単な傾向があります(一部のアーキテクチャでは、すべてのデータレジスタ操作後に自動的にテストされます)。
ジョーイアダムス

9
@burrito私は異論はありませんが、ゼロ以外の値で条件付けられたループは、通常、ほぼ完全に予測されます。
ダンカン

14
インクリメントとデクリメントは、おそらくすべてのプラットフォームで(おそらくx86で)等しく高速です。違いは、ループ終了条件のテストにあります。カウンターがゼロに達したかどうかを確認するには、実質的にフリーです。値をデクリメントすると、プロセッサーにゼロフラグが設定され、終了条件を検出するには、そのフラグを確認するだけで済みます。一方、終了条件の前に比較演算が必要な場合検出することができます。
レゴ

7
もちろん、最近のコンパイラーはループを自動的にベクトル化して逆にすることができるので、これらすべてが最近は無意味です。
ラムダ妖精

366
while( x-- > 0 )

それがどのように解析されるかです。


362

完全にオタクですが、私はこれを使用します:

#define as ;while

int main(int argc, char* argv[])
{
    int n = atoi(argv[1]);
    do printf("n is %d\n", n) as ( n --> 0);
    return 0;
}


1
これはコンパイルされません。CはPascalではなく、の内部do ... whileはステートメントリストです。Cではブロックなので、でなければなりませんdo { ... } while
ローン侯爵

25
@EJPコンパイルします。構文はdo statement while ( expression ) ;です。そうは言っても、私が冗談としての例を意味していることが理解されれば幸いです。
Escualo 2016年

321

私が読んだ1冊の本(どの本を正しく覚えていない)は次のように述べています

この場合、式:

x-->0

最大のトークンへの解析:

token 1: x
token 2: --
token 3: >
token 4: 0
conclude: x-- > 0

同じルールがこの式に適用されます。

a-----b

解析後:

token 1: a
token 2: --
token 3: --
token 4: -
token 5: b
conclude: (a--)-- - b

これが複雑な表現を理解するのに役立つことを願っています^^


98
2番目の説明は正しくありません。コンパイラはを見a-----bて考えますが(a--)-- - ba--は左辺値を返さないためコンパイルされません。
Tim Leaf

22
さらに、xおよび--は2つの別個のトークンです。
Roland Illig 2010

23
@DoctorT:レクサーを渡します。セマンティックパスのみがそのエラーを発生させることができます。彼の説明は正しいです。
v.oddou

9
-->演算子であると考える限り(これは、尋ねられた質問があることで暗示されることです)、この答えはまったく役に立ちません-トークン2は-->だけではなくであると考えるでしょう--。それが-->演算子ではないことがわかっている場合は、おそらく問題のコードを理解するのに問題がないので、まったく別の質問がない限り、これがどのように役立つかはよくわかりません。
ベルンハルト・バーカー、

4
@DoctorTの例はa、lvalueを返すポストデクリメント演算子がオーバーロードされていることを前提として正しい場合があります。coliru.stacked-crooked.com/a/e1effc351ae79e9f
ドキュメント

275

これはまったく同じです

while (x--)
{
   printf("%d ", x);
}

負でない数


153
これはいけませんfor(--x++;--x;++x--)か?
Mateen Ulhaq

9
@DoctorTそれunsignedが目的です
Cole Johnson

12
@MateenUlhaq、それは標準によると間違っています。式に--x++は1.9.15に従って未定義の動作があります
WorldSEnder

それがを使用していた場合unsigned、それは使用されたでしょう%u
Cacahuete Frito

242

とにかく、これで「移動先」オペレーターができました。"-->"方向として覚えやすいですし、「xがゼロになるまで」という意味はまっすぐです。

さらに、"for (x = 10; x > 0; x --)"一部のプラットフォームよりも少し効率的です。


17
特にxの値が負の場合は、常にtrueになることはありません。
ガネーシュゴパラスブラマニアン2009年

15
他のバージョンは同じことをしません- for (size_t x=10; x-->0; )ループの本体は9,8、..、0で実行されますが、他のバージョンは10,9、..、1を持っています。それ以外の場合は、符号なし変数を使用してループをゼロまで終了するのは非常に困難です。
ピートカーカム

4
これは少し誤解を招くと思います... ++>インクリメンタルな作業を行うには別のオペレーターが必要なため、文字通り「行く」演算子はありません。
tslmy 2013年

19
@ジョシュ:実際には、オーバーフローはに未定義の動作を与えるintので、x負の値から始めた場合、ゼロにするのと同じくらい簡単に犬を食べてしまう可能性があります。
SamB 2013

3
これは、@ PeteKirkhamからの意見で示されている理由から、私にとって非常に重要なイディオムです。私は、符号なしの量に対してループを減らす必要があるためです0。(比較のために、while (n--)unsignedの代わりに書くなど、ゼロのテストを省略するイディオムはn何も買わず、読みやすさを大幅に阻害します。)また、最初のインデックスより1つ多く指定するという快適なプロパティがあります。必要な場合(たとえば、配列のループの場合は、サイズを指定します)。-->スペースがないことも好きです。これにより、イディオムが認識しやすくなります。
Marc van Leeuwen

221

このコードは、最初にxと0を比較し、次にxをデクリメントします。(最初の回答でも述べたとおり:xをポストデクリメントしてから、xと0を>演算子と比較しています。)このコードの出力を参照してください。

9 8 7 6 5 4 3 2 1 0

ここで、最初に比較し、次に出力に0が表示されることで減分します。

最初にデクリメントしてから比較する場合は、次のコードを使用します。

#include <stdio.h>
int main(void)
{
    int x = 10;

    while( --x> 0 ) // x goes to 0
    {
        printf("%d ", x);
    }
    return 0;
}

その出力は次のとおりです。

9 8 7 6 5 4 3 2 1

177

このコードを実行すると、コンパイラが9876543210を出力します。

#include <iostream>
int main()
{
    int x = 10;

    while( x --> 0 ) // x goes to 0
    {
        std::cout << x;
    }
}

予想通り。while( x-- > 0 )実際に意味しますwhile( x > 0)x--ポストデクリメントx

while( x > 0 ) 
{
    x--;
    std::cout << x;
}

同じことを書く別の方法です。

オリジナルが「xが0になる間」のように見えるのは良いことです。


4
同じステートメントで同じ変数を複数回インクリメント/デクリメントしている場合、結果は未定義です。この状況には適用されません。
Tim Leaf

13
while( x-- > 0 ) actually means while( x > 0)-あなたがそこで何を言おうとしていたのかはわかり--ませんが、あなたがそれを言った方法は、意味がまったく意味がないことを意味します。これは明らかに非常に間違っています。
Bernhard Barker

@Dukelingから要点を引き出すために、この回答は元の投稿と同じではありません。元の投稿でx-1ループを抜けた後、この答えでxはになります0
マークラカタ

148

--との間にスペースがありません>xポストデクリメント、つまり条件をチェックした後にデクリメントされますx>0 ?


42
スペースはありません-C(++)は空白を無視します。

27
@ H2CO3これは一般的には当てはまりません。空白はトークンを区切るために使用されなければならない場所、例えばであり、#define foo()対が#define foo ()
イェンス

30
@Jens方法:「スペースが欠落していない-C(++)は不要な空白を無視します。」?
ケビンP.ライス

139

--デクリメント演算子で>あり、より大演算子です。

2つの演算子は、のように1つの演算子として適用され-->ます。


11
それらは、2つの別々の演算子として適用されます。それらは「単一のもの」のように見えるように誤解を招くようにのみ書かれています。
underscore_d

129

2つの演算子の組み合わせです。1つ目--は、値を減らすことです。>、値が右側のオペランドより大きいかどうかをチェックするためのものです。

#include<stdio.h>

int main()
{
    int x = 10;

    while (x-- > 0)
        printf("%d ",x);

    return 0;
}

出力は次のようになります。

9 8 7 6 5 4 3 2 1 0            

122

実際にxは、減少後の状態であり、その状態がチェックされています。そうではありません-->、それはです、(x--) > 0

注:の値はx、条件のチェック後に変更されます。同様のケースもいくつか発生する可能性があります。次に例を示します。

-->    x-->0
++>    x++>0
-->=   x-->=0
++>=   x++>=0

6
ただし、++>はwhile()ではほとんど使用できません。「上へ行く」演算子は++ <であり、どこよりも見栄えがしません。演算子->は幸せな偶然です。
フロリアンF

2
@BenLeggieroこれは、何かをするコードを生成するという意味では(うまくいく)(偽の賢いコードが嫌いな読者を苛立たせながら)可能性がありますが、意味論は異なります。不自然な例として、x1から開始するとループ本体は実行されませんが、実行されwhile ( (x--) > 0 )ます。{編集} Eric LippertがC#4のリリースノートで両方をカバーしました:blogs.msdn.microsoft.com/ericlippert/2010/04/01/…–
underscore_d

120

CおよびC ++は「最大のムンク」ルールに従います。a --- bがに翻訳されるのと同じ方法(a--) - bで、あなたのケースでは次のようにx-->0翻訳されます (x--)>0

ルールの本質は、左から右に向かって、有効な式を形成する文字の最大数をとることによって式が形成されるということです。


5
これはOPが想定したものです。「((a)->)」が最大のムンクでした。OPの元の仮定が正しくなかったことがわかります。「->」は最大の有効な演算子ではありません。
david 2014

4
私が正しく思い出す場合、貪欲な構文解析とも呼ばれます。
ロイティンカー

1
@RoyTinker貪欲なスキャン。パーサーはこれとは何の関係もありません。
ローン侯爵

27

なぜすべての複雑化?

元の質問に対する簡単な答えは次のとおりです。

#include <stdio.h>
int main()
{
    int x = 10;
    while (x > 0) 
    {
        printf("%d ", x);
        x = x-1;
    }
}

同じことをします。このようにするべきだと言っているわけではありませんが、同じことを行い、1つの投稿で質問に回答することになります。

x--上記のためだけ速記であり、>普通より大でありますoperator。大きな謎はありません!

今日では単純なことを複雑にする人が多すぎます;)


17
この質問は合併症ではなく、** C ++ / STLの隠し機能とダークコーナー**に関するものです
pix

20
ここのxはprintfの後に減分されるため、ここのプログラムは元の出力とは異なる出力を提供します。これは、「単純な答え」が通常どのように正しくないかをよく示しています。
OO Tiib

2
The OP's way: 9 8 7 6 5 4 3 2 1 0およびThe Garry_G way: 10 9 8 7 6 5 4 3 2 1
Anthony、

2
同じことはしません。x=x-1前に動かすprintfと、「同じことをする」と言うことができます。
CITBL

26

従来の方法では、whileループ括弧内の()条件と中括弧内の終了条件を定義しました{}-->、両方を同時に定義しました。

例えば:

int abc(void)
{
    int a = 5
    while((a--) > 0) // Decrement and comparison both at once
    {
        // Code
    }
}

これは、がより大きいaときにループをデクリメントして実行します。a0

従来は次のようになります。

int abc(void)
{
    int a = 5;
    while(a > 0)
    {
        a--;
        // Code
    }
    a--;
}

どちらの方法でも、同じことをして同じ目標を達成します。


5
これは誤りです。問題のコードは「test-write-execute」(最初にテストし、新しい値を書き込み、ループを実行する)を実行します。例は「test-execute-write」です。
v010dya 2017

@ v010dya答えを修正しました、今test-write-executeは質問のようになりました、指摘してくれてありがとう!
コタウカス

@VladislavToncharovあなたの編集はまだ間違っていました。私を参照してください。
SS

8

(x --> 0) 手段 (x-- > 0)

  1. あなたは使うことができます (x -->)
    output -: 9 8 7 6 5 4 3 2 1 0

  2. あなたは(-- x > 0) それを使うことができます(--x > 0)
    output -: 9 8 7 6 5 4 3 2 1

  3. あなたは使うことができます
(--\
    \
     x > 0)

output -: 9 8 7 6 5 4 3 2 1

  1. あなたは使うことができます
(\
  \
   x --> 0)

output -: 9 8 7 6 5 4 3 2 1 0

  1. あなたは使うことができます
(\
  \
   x --> 0
          \
           \
            )

output -: 9 8 7 6 5 4 3 2 1 0

  1. あなたも使うことができます
(
 x 
  --> 
      0
       )

output -: 9 8 7 6 5 4 3 2 1 0

同様に、このコマンドを正常に実行するために多くの方法を試すことができます

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