Co to jest operator „->” w C ++?


8922

Po przeczytaniu ukrytych funkcji i zakamarkach C ++ / STL na comp.lang.c++.moderated, byłem całkowicie zaskoczony, że następujący fragment skompilowany i pracował zarówno w Visual Studio 2008 i G ++ 4.4.

Oto kod:

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

Wynik:

9 8 7 6 5 4 3 2 1 0

Zakładam, że to C, ponieważ działa również w GCC. Gdzie jest to zdefiniowane w standardzie i skąd ono pochodzi?


503
A może po prostu właściwe odstępy ... Nie sądzę, żebym kiedykolwiek widziała spację między zmienną a jednym ++albo --wcześniej ...
Matthew Scharley,

1154
Ten operator „idzie do” można cofnąć (0 <- x). Jest też operator „biegnie do” (0 <---- x). Rany, najśmieszniejsza rzecz, jaką kiedykolwiek słyszałem o składni c ++ =) +1 dla pytania.
SadSido,

233
Co zabawne, chociaż interpretacja jest bardzo błędna, opisuje ona, co robi kod poprawnie. :)
Noldorin,

811
Wyobraź sobie nowe możliwości składniowe: #define upto ++<, #define downto -->. Jeśli czujesz się źle, możesz zrobić #define for while(i #define do ) {(i #define done ;}) napisać for x downto 0 do printf("%d\n", x) doneOch, ludzkość ...
Chris Lutz

98
Otwiera możliwość zupełnie nowego ekspresyjnego sposobu kodowania, wart poświęcenia kilku ostrzeżeń kompilatora dla: bool CheckNegative (int x) {return x <0? prawda fałsz ); }
ttt

Odpowiedzi:


8596

-->nie jest operatorem. W rzeczywistości są to dwa oddzielne operatory --i >.

Kod warunku zmniejsza się x, zwracając xoryginalną (nie zmniejszoną) wartość, a następnie porównuje oryginalną wartość za 0pomocą >operatora.

Aby lepiej zrozumieć, oświadczenie można napisać w następujący sposób:

while( (x--) > 0 )

262
Z drugiej strony wygląda to jak operator zasięgu w tym kontekście.
Charles Salvia

109
Mówiąc, że x jest post-zmniejszany, a następnie w porównaniu do 0 jest taka sama jak mówi X jest zmniejszana po porównaniu do 0
Charles Salvia

8
Nie sądzę, że to samo. Myślę, że słowo „następnie” oznacza, że ​​istnieje kolejność (po zmniejszeniu po wartości x jest o jeden mniejsza). Myślę, że można powiedzieć: „Zmniejszasz x, a następnie porównujesz jego starą wartość i 0 ...”, aby było jaśniej. Ale i tak to dręczy. Wszyscy wiemy, co to znaczy.
Johannes Schaub - litb

38
W Javie również się kompiluje :)
Steven Devijver,

44
Jego nazwa, @Jay, to zły styl programowania :-) Dowodzi tego fakt, że pytanie zostało zadane w pierwszej kolejności. Znacznie sensowniej jest powiązać tekstowo operatorów z rzeczami, na których działają, niż z czymś niezwiązanym, więc while (x-- > 0)byłoby bardziej trafne. Sprawia również, że bardziej oczywiste jest to, co się dzieje (przynajmniej w edytorze czcionek o stałej czcionce), co oznacza, że ​​nawiasy w tej odpowiedzi nie byłyby konieczne.
paxdiablo

3130

Lub coś zupełnie innego ... xslajdy do 0.

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

Nie tak matematyczne, ale ... każdy obraz maluje tysiąc słów ...


216
@mafutrct - Jak pamiętam, \ w C po prostu dołącza następny wiersz, tak jakby nie było podziału linii. Tu w zasadzie nic nie rób.
Hogan,

2
@mafu znak „\” informuje kompilator, że bieżąca linia jest kontynuowana w następnym wierszu, więc kompilator powinien połączyć obie linie i skompilować jako jedną. „while (x -> 0)” będzie działać, dopóki x nie będzie równe -1. Ale sposób, w jaki tworzy wcięcia, sprawia, że ​​x przesuwa się do zera. „Podczas gdy x przesuwa się do 0 ...”
Felype

16
IIRC, K&R C pozwoliły na spację między „-s” w operatorze dekrementacji, w którym to przypadku możesz mieć odwrotne ukośniki w środku, co wyglądałoby jeszcze lepiej. :)
Jules

10
@ArnavBorborah to stare znaczenie wyrażenia why waste words when a picture does a better job, używane w tym kontekście jako żart. (w rzeczywistości są 2 słowa kluczowe whilei printf)
niezsynchronizowany

87
Ach tak, niejasny operator slajdów. Jak mogłem zapomnieć!
demonkoryu


1277

Jest to równoważne z

while (x-- > 0)

x--(po zmniejszeniu) jest równoważne, x = x-1więc kod przekształca się w:

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

15
To nie do końca prawda. Wartość x wewnątrz ciała pętli jest inna w drugim przypadku. Instrukcja przypisania w twoim przykładzie powinna być powyżej logiki, aby była równoważna. Postfiks - odejmuje 1, ale porównanie nastąpi z wartością sprzed odejmowania.
uliwitness

4
@uliwitness Są to naprawdę równoważne. Byłoby błędem, gdyby używany był prefiks: 0 >-- xW tym przypadku xjest zmniejszany przed logiką. W postfiksie logika jest wykonywana przed zmniejszeniem, a zatem obie próbki są równoważne. Możesz je napisać Consolei przetestować.
Emma

12
Nadal nie są równoważne. Po pierwszej pętli x wynosi -1 (lub przepełnia się, jeśli nie jest podpisany), po drugiej - 0. (Zakładając, że x zaczyna się od nieujemnej, żadna pętla nie modyfikuje x, nie łamie się lub…)
Caesar

1
while(x=x-1,x+1 > 0)jest równoważne.
SS Anne

2
@Shebham, oto przykład licznika: jeśli x zaczyna się od 0, pod oryginalną pętlą kończy się jako -1, w twojej wersji pozostanie zero. Więc nie są równoważne.
Elliott

1208

x może zerować nawet szybciej w przeciwnym kierunku:

int x = 10;

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

8 6 4 2

Możesz kontrolować prędkość za pomocą strzałki!

int x = 100;

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

90 80 70 60 50 40 30 20 10

;)


6
który system operacyjny, ten typ danych wyjściowych wygenerowano, używam Ubuntu 12.04, ponieważ miałem komunikat o błędzie
Bhuvanesh

74
Chociaż powinno to być oczywiste, dla wszystkich początkujących w C ++ czytających to: nie rób tego. Po prostu użyj przypisania rozszerzonego, jeśli musisz zwiększyć / zmniejszyć o więcej niż jeden.
Blimeo,

263
Zero z „laserami”. podczas gdy (0> - - - - - - - - - - ---------- x) ... to samo wyjście.
Samuel Danielson,

4
@phord czy na pewno nie można go skompilować? -> coliru.stacked-crooked.com/a/5aa89a65e3a86c98
doc

18
@doc Kompiluje się w c ++, ale nie w c.
phord

548

Jego

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

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

       printf("%d ", x);
     }

     return 0;
}

Tylko przestrzeń sprawia, że ​​rzeczy wyglądają zabawnie, --dekrety i >porównania.


431

Zastosowanie -->ma znaczenie historyczne. Zmniejszanie było (i nadal jest w niektórych przypadkach) szybsze niż zwiększanie w architekturze x86. Korzystanie -->sugeruje, że xtak będzie 0, i przemawia do osób z wykształceniem matematycznym.


479
Niezupełnie prawda. Zmniejszanie i zwiększanie zajmuje tyle samo czasu, zaletą tego jest to, że porównanie do zera jest bardzo szybkie w porównaniu do porównania względem zmiennej. Dotyczy to wielu architektur, nie tylko x86. Wszystko z instrukcją JZ (skok, jeśli zero). Grzebiąc wokoło, można znaleźć wiele pętli „for” zapisanych wstecz, aby zapisać cykle w porównaniu. Jest to szczególnie szybkie w przypadku x86, ponieważ czynność zmniejszania zmiennej ustawia odpowiednio flagę zero, dzięki czemu można następnie rozgałęziać bez konieczności jawnego porównywania zmiennej.
burito

25
Cóż, zmniejszanie w kierunku zera oznacza, że ​​musisz tylko porównywać z 0 na iterację pętli, podczas gdy iteracja w kierunku n oznacza porównywanie z n każdej iteracji. Ten pierwszy jest zwykle łatwiejszy (a na niektórych architekturach jest automatycznie testowany po każdej operacji rejestracji danych).
Joey Adams,

9
@burrito Mimo, że się nie zgadzam, pętle warunkowane na wartości niezerowe zwykle są przewidywane prawie idealnie.
Duncan

14
Przyrosty i spadki są równie szybkie, prawdopodobnie na wszystkich platformach (zdecydowanie na x86). Różnica polega na testowaniu stanu końca pętli. Aby sprawdzić, czy licznik osiągnął zero, jest praktycznie wolne - gdy zmniejszasz wartość, w procesorze ustawiana jest flaga zerowa i aby wykryć warunek końcowy, wystarczy sprawdzić tę flagę, podczas gdy podczas zwiększania wymagana jest operacja porównania przed warunkiem końcowym można wykryć.
LEGO

7
Oczywiście wszystko to jest obecnie dyskusyjne, ponieważ nowoczesne kompilatory mogą automatycznie wektoryzować i odwracać pętle.
Lambda Fairy


362

Zupełnie maniakiem, ale użyję tego:

#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;
}

17
@SAFX - Byłoby idealnie hieroglifami z nawiasami egipskimi
mouviciel

1
To się nie kompiluje. C to nie Pascal, którego wnętrze do ... whileto lista instrukcji. W C jest to blok, więc musi być do { ... } while.
user207421,

25
@EJP kompiluje się. Składnia jest następująca do statement while ( expression ) ;. Powiedziawszy to, mam nadzieję, że zrozumiałem, że miałem na myśli ten przykład jako żart.
Escualo

321

W jednej książce, którą przeczytałem (nie pamiętam poprawnie, którą książkę) napisałem: Kompilatory próbują parsować wyrażenia do największego tokena przy użyciu reguły lewej i prawej.

W takim przypadku wyrażenie:

x-->0

Analizuje największe tokeny:

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

Ta sama reguła dotyczy tego wyrażenia:

a-----b

Po analizie:

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

Mam nadzieję, że to pomoże zrozumieć skomplikowane wyrażenie ^^


98
Twoje drugie wyjaśnienie jest nieprawidłowe. Kompilator zobaczy a-----bi pomyśli (a--)-- - b, co nie kompiluje, ponieważ a--nie zwraca wartości.
Tim Leaf

22
Dodatkowo, xa --dwa oddzielne żetony.
Roland Illig

23
@DoctorT: przechodzi leksykon. tylko semantyczny przebieg może wyemitować ten błąd. więc jego wyjaśnienie jest prawidłowe.
v.oddou

9
Tak długo, jak myślisz, że -->jest operatorem (co sugeruje pytanie, które zostało zadane), odpowiedź ta w ogóle nie jest pomocna - pomyślisz, że token 2 jest -->nie tylko --. Jeśli wiesz, że -->to nie jest operator, prawdopodobnie nie masz problemu ze zrozumieniem kodu w pytaniu, więc jeśli nie masz zupełnie innego pytania, nie jestem pewien, jak to może być przydatne.
Bernhard Barker

4
Przykład @DoctorT może być poprawny, zakładając, że aprzeciążono operatora po dekrementacji, który zwraca wartość. coliru.stacked-crooked.com/a/e1effc351ae79e9f
doc

275

To jest dokładnie to samo co

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

dla liczb nieujemnych


153
Nie powinno tak być for(--x++;--x;++x--)?
Mateen Ulhaq,

9
@DoctorT po to unsignedjest
Cole Johnson

12
@MateenUlhaq, co jest błędne zgodnie ze standardem, wyrażenie --x++ma niezdefiniowane zachowanie zgodnie z §1.9.15
WorldSEnder

Gdyby używał unsigned, użyłby%u
Cacahuete Frito

242

W każdym razie mamy teraz operatora „idzie do”. "-->"łatwo jest zapamiętać go jako kierunek, a „gdy x idzie do zera” jest proste w znaczeniu.

Ponadto jest nieco bardziej wydajny niż "for (x = 10; x > 0; x --)"na niektórych platformach.


17
Nie może być prawdą zawsze, szczególnie gdy wartość x jest ujemna.
Ganesh Gopalasubramanian

15
Druga wersja nie robi tego samego - z for (size_t x=10; x-->0; )treścią pętli jest wykonywana za pomocą 9,8, .., 0, podczas gdy druga wersja ma 10,9, .., 1. W przeciwnym razie wyjście z pętli do zera przy użyciu zmiennej bez znaku jest dość trudne.
Pete Kirkham,

4
Myślę, że jest to trochę mylące ... Nie mamy dosłownie operatora „idzie do”, ponieważ potrzebujemy innego, ++>aby wykonać pracę przyrostową.
tslmy

19
@Josh: w rzeczywistości przepełnienie daje niezdefiniowane zachowanie int, więc może równie dobrze zjadać twojego psa, jak zerować, xjeśli zaczyna się negatywnie.
SamB,

3
Jest to dla mnie bardzo ważny idiom z powodu podanego w comnmet przez @PeteKirkham, ponieważ często muszę robić pętle malejące nad niepodpisanymi ilościami aż do samego końca 0. (Dla porównania, idiom pomijania testów zerowych, takich jak pisanie while (n--)zamiast dla niepodpisanych n, nic ci nie kupuje, a dla mnie znacznie utrudnia czytelność.) Ma również przyjemną właściwość, że podajesz jeden więcej niż indeks początkowy, który zwykle jest tym, co chcesz (np. dla pętli nad tablicą określasz jej rozmiar). Lubię też -->bez miejsca, ponieważ ułatwia to rozpoznanie tego idiomu.
Marc van Leeuwen,

221

Ten kod najpierw porównuje x i 0, a następnie zmniejsza x. (Powiedział również w pierwszej odpowiedzi: dekrementujesz x, a następnie porównujesz x i 0 z >operatorem). Zobacz wynik tego kodu:

9 8 7 6 5 4 3 2 1 0

Teraz najpierw porównujemy, a następnie zmniejszamy, widząc 0 w danych wyjściowych.

Jeśli chcemy najpierw zmniejszyć, a następnie porównać, użyj tego kodu:

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

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

Ten wynik to:

9 8 7 6 5 4 3 2 1

177

Mój kompilator wydrukuje 9876543210 po uruchomieniu tego kodu.

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

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

Zgodnie z oczekiwaniami. W while( x-- > 0 )rzeczywistości oznacza while( x > 0). x--Postu zmniejsza x.

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

to inny sposób pisania tego samego.

Fajnie, że oryginał wygląda jak „podczas gdy x idzie do 0”.


4
Wynik jest niezdefiniowany tylko wtedy, gdy inkrementujesz / dekrementujesz tę samą zmienną więcej niż raz w tej samej instrukcji. Nie dotyczy to tej sytuacji.
Tim Leaf,

13
while( x-- > 0 ) actually means while( x > 0)- Nie jestem pewien, co tam próbowałeś powiedzieć, ale sposób, w jaki to powiedziałeś, oznacza, że --nie ma żadnego znaczenia, co oczywiście jest bardzo błędne.
Bernhard Barker

Aby doprowadzić punkt do domu z @Dukeling, ta odpowiedź nie jest taka sama jak oryginalny post. W oryginalnym poście xbędzie -1po opuszczeniu pętli, podczas gdy w tej odpowiedzi xbędzie 0.
Mark Lakata

148

Brakuje miejsca między --i >. xjest po zmniejszeniu, to znaczy zmniejszeniu po sprawdzeniu warunku x>0 ?.


42
Nie brakuje miejsca - C (++) ignoruje białe znaki.

27
@ H2CO3 Ogólnie nie jest to prawdą. Są miejsca, w których należy oddzielić białe znaki do oddzielenia żetonów, np. W #define foo()porównaniu #define foo ().
Jens

30
@Jens Co powiesz na: „Nie brakuje miejsca - C (++) ignoruje niepotrzebne białe znaki.”?
Kevin P. Rice,

139

--jest operatorem dekrementacji i >jest operatorem większym niż .

Oba operatory są stosowane jako jeden podobny -->.


11
stosowane jako 2 oddzielne operatory, którymi są. Zostały napisane w sposób wprowadzający w błąd, aby wyglądać jak „jeden”.
underscore_d

129

To połączenie dwóch operatorów. Pierwszy --służy do zmniejszania wartości i >do sprawdzania, czy wartość jest większa niż operand po prawej stronie.

#include<stdio.h>

int main()
{
    int x = 10;

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

    return 0;
}

Dane wyjściowe będą:

9 8 7 6 5 4 3 2 1 0            

122

W rzeczywistości xjest po dekrementacji iz tym warunkiem jest sprawdzany. Nie -->jest(x--) > 0

Uwaga: wartość parametru xzmienia się po sprawdzeniu warunku, ponieważ zmniejsza się on po dekrementacji. Mogą wystąpić również podobne przypadki, na przykład:

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

6
Tyle, że ++> nie może być używane przez chwilę (). Operator „idzie do ...” to ++ <, co nigdzie nie wygląda tak ładnie. Operator -> to szczęśliwy zbieg okoliczności.
Florian F

2
@BenLeggiero To może „działać” w sensie generowania kodu, który coś robi (jednocześnie denerwując czytelników, którzy nie lubią fałszywego kodu), ale semantyka jest inna, ponieważ jego użycie predekrementacji oznacza, że ​​wykona jedną mniej iteracji. Jako wymyślony przykład, nigdy nie wykonałby ciała pętli, gdyby xzaczął się od 1, ale while ( (x--) > 0 )by to zrobił. {edit} Eric Lippert opisał
underscore_d

120

C i C ++ przestrzegają zasady „maksymalnego chrupania”. Taki sam sposób, w jaki a --- b jest tłumaczony (a--) - b, w twoim przypadku x-->0oznacza (x--)>0.

Zasada mówi w zasadzie, że od lewej do prawej, wyrażenia są tworzone przez wzięcie maksymalnej liczby znaków, które utworzą prawidłowe wyrażenie.


5
Tak właśnie zakładał PO: że „((a) ->)” było maksymalnym chrupnięciem. Okazuje się, że pierwotne założenie PO było niepoprawne: „->” nie jest maksymalnym poprawnym operatorem.
David

4
Znany również jako chciwy parsowanie, jeśli dobrze pamiętam.
Roy Tinker,

1
@RoyTinker Chciwe skanowanie. Parser nie ma z tym nic wspólnego.
user207421,

27

Skąd ta komplikacja?

Prosta odpowiedź na pierwotne pytanie brzmi:

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

Robi to samo. Nie mówię, że powinieneś to zrobić w ten sposób, ale robi to samo i odpowiedziałby na pytanie w jednym poście.

Jest x--to po prostu skrót od powyższego, i >jest po prostu normą większą niż operator. Bez wielkiej tajemnicy!

Zbyt wielu ludzi komplikuje proste rzeczy;)


17
To pytanie nie dotyczy komplikacji, ale ** Ukrytych funkcji i Mrocznych zakątków C ++ / STL **
pix

20
Program tutaj daje inne wyjście niż oryginał, ponieważ x jest zmniejszane po printf. To dobrze pokazuje, jak „proste odpowiedzi” są zwykle nieprawidłowe.
Öö Tiib

2
The OP's way: 9 8 7 6 5 4 3 2 1 0orazThe Garry_G way: 10 9 8 7 6 5 4 3 2 1
Anthony

2
To nie robi tego samego. Przenieś swój x=x-1wcześniej, printfwtedy możesz powiedzieć „robi to samo”.
CITBL

26

W konwencjonalny sposób zdefiniowalibyśmy warunek w whilenawiasie pętli ()i warunek kończący w nawiasach {}, ale -->definiujemy oba jednocześnie.

Na przykład:

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

To zmniejsza ai uruchamia pętlę, gdy ajest większa niż 0.

Konwencjonalnie wyglądałoby to tak:

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

Oba sposoby robimy to samo i osiągamy te same cele.


5
To jest niepoprawne. Kod w pytaniu brzmi: „test-write-execute” (najpierw test, wpisz nową wartość, wykonaj pętlę), twój przykład to „test-execute-write”.
v010dya

@ v010dya Naprawiono odpowiedź, teraz jest test-write-executejak w pytaniu, dziękuję za zwrócenie uwagi!
Kotauskas,

@VladislavToncharov Twoja edycja była nadal nieprawidłowa. Zobacz moje
SS Anne

8

(x --> 0) znaczy (x-- > 0)

  1. możesz użyć (x -->)
    output -: 9 8 7 6 5 4 3 2 1 0

  2. możesz użyć (-- x > 0) To znaczy(--x > 0)
    output -: 9 8 7 6 5 4 3 2 1

  3. możesz użyć
(--\
    \
     x > 0)

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

  1. możesz użyć
(\
  \
   x --> 0)

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

  1. możesz użyć
(\
  \
   x --> 0
          \
           \
            )

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

  1. możesz także użyć
(
 x 
  --> 
      0
       )

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

podobnie możesz wypróbować wiele metod, aby pomyślnie wykonać to polecenie

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.