Qu'est-ce que l'opérateur «->» en C ++?

8927

Après avoir lu les fonctions cachées et coins sombres de C ++ / STL sur comp.lang.c++.moderated, j'ai été complètement surpris que le suivant extrait compilé et a travaillé dans Visual Studio 2008 et G ++ 4.4.

Voici le code:

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

Production:

9 8 7 6 5 4 3 2 1 0

Je suppose que c'est C, car cela fonctionne également dans GCC. D'où cela est-il défini dans la norme et d'où vient-il?

GManNickG
la source
503
Ou même juste un bon espacement ... Je ne pense pas avoir jamais vu d'espace entre la variable et l'une ++ou l' autre ou --avant ...
Matthew Scharley
1155
Cet opérateur "va à" peut être inversé (0 <- x). Et il y a aussi un opérateur "run to" (0 <---- x). Décidément, la chose la plus drôle que j'ai jamais entendu parler de la syntaxe c ++ =) +1 pour la question.
SadSido
233
Curieusement, bien que l'interprétation soit très fausse, elle décrit ce que le code fait correctement. :)
Noldorin
811
Imaginez les nouvelles possibilités de syntaxe: #define upto ++<, #define downto -->. Si vous vous sentez mal, vous pouvez le faire #define for while(et #define do ) {(et #define done ;}) et écrire for x downto 0 do printf("%d\n", x) doneOh, l'humanité ...
Chris Lutz
98
Ouvre la possibilité d'une toute nouvelle façon expressive de codage, qui vaut la peine de sacrifier quelques avertissements du compilateur pour: bool CheckNegative (int x) {return x <0? vrai faux ); }
ttt

Réponses:

8606

-->n'est pas un opérateur. Il s'agit en fait de deux opérateurs distincts, --et >.

Le code du conditionnel diminue x, tout en renvoyant xla valeur d'origine (non décrémentée) de, puis compare la valeur d'origine avec l' 0utilisation de l' >opérateur.

Pour mieux comprendre, la déclaration pourrait être rédigée comme suit:

while( (x--) > 0 )
pomme de terre
la source
262
Là encore, cela ressemble à une sorte d'opérateur de plage dans ce contexte.
Charles Salvia
109
Dire que x est post-décrémenté puis comparé à 0 revient à dire que x est décrémenté après avoir été comparé à 0
Charles Salvia
8
Je ne pense pas que ce soit pareil. Je pense que le mot "alors" implique qu'il y a un ordre (après décrémentation, la valeur de x est un de moins). Je pense que l'on peut dire "Vous postez décrémenter x puis comparez son ancienne valeur et 0 ..." pour le rendre plus clair. Mais c'est de toute façon un peu marrant. Nous savons tous ce que cela signifie.
Johannes Schaub - litb
38
En Java, il compile également :)
Steven Devijver
44
Son nom, @Jay, est un mauvais style de programmation :-) Ceci est démontré par le fait que la question a été posée en premier lieu. Il est beaucoup plus logique de lier textuellement des opérateurs à la chose sur laquelle ils opèrent plutôt qu'à quelque chose de non lié, ce while (x-- > 0)serait donc plus approprié. Cela rend également plus clair ce qui se passe (au moins dans un éditeur de polices fixes), ce qui signifie que les parenthèses dans cette réponse ne seraient pas nécessaires.
paxdiablo
3132

Ou pour quelque chose de complètement différent ... x glisse vers 0.

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

Pas si mathématique, mais ... chaque image vaut mille mots ...

non synchronisés
la source
216
@mafutrct - Si je me souviens bien, \ en C ajoute simplement la ligne suivante comme s'il n'y avait pas de saut de ligne. Les \ s ici ne font rien du tout.
Hogan
2
@mafu le caractère '\' indique au compilateur que la ligne actuelle continue dans la ligne suivante et donc le compilateur doit fusionner les deux lignes et compiler comme une seule. 'while (x -> 0)' s'exécutera jusqu'à ce que x soit égal à -1. Mais, la façon dont il fait les indentations donne l'impression que x glisse à zéro. 'Alors que x glisse à 0 ...'
Felype
16
IIRC, K&R C a permis des espaces entre les 'dans l'opérateur de décrémentation, auquel cas vous pourriez avoir les barres obliques inverses au milieu, ce qui serait encore plus cool. :)
Jules
10
@ArnavBorborah, c'est une ancienne expression why waste words when a picture does a better job, utilisée comme plaisanterie dans ce contexte. (il y a en fait 2 mots while- clés et printf)
non synchronisé
88
Ah oui, l'opérateur de diapositive obscure. Comment pourrai-je oublier!
demonkoryu
2378

C'est un opérateur très compliqué, donc même ISO / IEC JTC1 (Joint Technical Committee 1) a placé sa description dans deux parties différentes de la norme C ++.

Blague à part, ce sont deux opérateurs différents: --et >décrits respectivement au §5.2.6 / 2 et au §5.9 de la norme C ++ 03.

Kirill V. Lyadvinsky
la source
1278

C'est équivalent à

while (x-- > 0)

x--(post décrément) est équivalent à x = x-1so, le code se transforme en:

while(x > 0) {
    x = x-1;
    // logic
}
x--;   // The post decrement done when x <= 0
Shubham
la source
15
Ceci n'est pas tout à fait vrai. La valeur de x à l'intérieur du corps de boucle est différente dans le second cas. L'instruction d'affectation dans votre exemple doit être au - dessus de la logique pour qu'elle soit équivalente. Postfix - soustrait 1, mais la comparaison se fera avec la valeur d' avant la soustraction.
uliwitness
4
@uliwitness Ce sont vraiment équivalents. Ce serait faux si le préfixe était utilisé: 0 >-- xdans ce cas, il xest décrémenté avant la logique. En postfixe, la logique est exécutée avant la décrémentation et donc les deux échantillons sont équivalents. N'hésitez pas à les écrire dans un Consoleet à les tester.
Candleshark
12
Ils ne sont toujours pas équivalents. Après la première boucle, x est -1 (ou survolé s'il n'est pas signé), après la seconde, il est 0. (En supposant que x commence non négatif, aucune boucle ne modifie x ou ne se casse ou…)
César
1
while(x=x-1,x+1 > 0)est équivalent.
SS Anne
2
@Shebham, voici un exemple de compteur: si x commence comme 0, sous la boucle d'origine il sortirait comme -1, avec votre version il resterait zéro. Ils ne sont donc pas équivalents.
Elliott
1210

x peut aller à zéro encore plus vite dans la direction opposée:

int x = 10;

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

8 6 4 2

Vous pouvez contrôler la vitesse avec une flèche!

int x = 100;

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

90 80 70 60 50 40 30 20 10

;)

doc
la source
6
quel système d'exploitation, ce type de sortie généré, j'utilise un ubuntu 12.04 en ce que j'avais un message d'erreur
Bhuvanesh
74
Bien que cela doive être évident, pour tous les débutants en C ++, lisez ceci: ne le faites pas. Utilisez simplement l'affectation augmentée si vous devez augmenter / diminuer de plus d'un.
Blimeo
263
Zéro avec des "lasers". tant que (0> - - - - - - - - - - ---------- x) ... même sortie.
Samuel Danielson
4
@phord êtes-vous sûr qu'il ne compile pas? -> coliru.stacked-crooked.com/a/5aa89a65e3a86c98
doc
18
@doc Il compile en c ++, mais pas en c.
phord
549

Ses

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

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

       printf("%d ", x);
     }

     return 0;
}

L'espace juste rend les choses drôles, --diminue et >compare.

RageZ
la source
432

L'utilisation de -->a une pertinence historique. La décrémentation était (et l'est toujours dans certains cas), plus rapide que l'incrémentation sur l'architecture x86. L'utilisation -->suggère que cela xva 0et fait appel à ceux qui ont des antécédents mathématiques.

Matt Joiner
la source
479
Pas exactement vrai. La décrémentation et l'incrémentation prennent le même temps, l'avantage de cela est que la comparaison à zéro est très rapide par rapport à la comparaison par rapport à une variable. Cela est vrai pour de nombreuses architectures, pas seulement pour x86. N'importe quoi avec une instruction JZ (saut si zéro). En fouillant, vous pouvez trouver de nombreuses boucles "for" qui sont écrites à l'envers pour enregistrer des cycles sur la comparaison. Ceci est particulièrement rapide sur x86 car l'acte de décrémentation de la variable définit le drapeau zéro de manière appropriée, de sorte que vous pouvez ensuite créer une branche sans avoir à comparer explicitement la variable.
burito
25
Eh bien, décrémenter vers zéro signifie que vous n'avez qu'à comparer avec 0 par itération de boucle, alors qu'itérer vers n signifie comparer avec n chaque itération. Le premier a tendance à être plus facile (et sur certaines architectures, il est automatiquement testé après chaque opération de registre de données).
Joey Adams
9
@burrito Bien que je ne sois pas en désaccord, les boucles conditionnées à des valeurs non nulles sont généralement prédites presque parfaitement.
Duncan
14
L'incrémentation et la décrémentation sont également rapides, probablement sur toutes les plateformes (certainement sur x86). La différence réside dans le test de la condition de fin de boucle. Pour voir si le compteur a atteint zéro est pratiquement gratuit - lorsque vous décrémentez une valeur, un indicateur zéro est défini dans le processeur et pour détecter la condition de fin, il vous suffit de vérifier cet indicateur tandis que lorsque vous incrémentez une opération de comparaison est requise avant la condition de fin peut être détecté.
lego
7
Bien sûr, tout cela est théorique ces jours-ci, car les compilateurs modernes peuvent vectoriser et inverser automatiquement les boucles.
Lambda Fairy
367
while( x-- > 0 )

est comment c'est analysé.

Grumdrig
la source
363

Complètement geek, mais je vais utiliser ceci:

#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;
}
Arrieta
la source
17
@SAFX - Ce serait parfaitement hiéroglyphique avec des parenthèses égyptiennes
mouviciel
1
Cela ne compile pas. C n'est pas Pascal, où l'intérieur de do ... whileest une liste d'instructions. En C, c'est un bloc, donc ça doit l'être do { ... } while.
Marquis de Lorne
25
@EJP il compile. La syntaxe est do statement while ( expression ) ;. Cela dit, j'espère qu'il est entendu que je voulais dire l'exemple comme une blague.
Escualo
321

Un livre que j'ai lu (je ne me souviens pas correctement du livre) a déclaré: Les compilateurs essaient d'analyser les expressions sur le plus gros jeton en utilisant la règle de gauche à droite.

Dans ce cas, l'expression:

x-->0

Analyse les plus gros jetons:

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

La même règle s'applique à cette expression:

a-----b

Après l'analyse:

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

J'espère que cela aide à comprendre l'expression compliquée ^^

NguyenDat
la source
98
Votre deuxième explication n'est pas correcte. Le compilateur verra a-----bet réfléchira (a--)-- - b, qui ne compile pas car a--ne renvoie pas de valeur l.
Tim Leaf
22
En outre, xet --sont deux jetons distincts.
Roland Illig
23
@DoctorT: il passe le lexer. seul le passage sémantique est capable d'émettre cette erreur. donc son explication est correcte.
v.oddou
9
Tant que vous pensez -->être un opérateur (ce qui implique que la question a été posée), cette réponse n'est pas du tout utile - vous penserez que le jeton 2 l'est -->, pas seulement --. Si vous savez que ce -->n'est pas un opérateur, vous n'avez probablement pas de problème à comprendre le code dans la question, donc, à moins que vous n'ayez une question complètement différente, je ne sais pas vraiment comment cela pourrait être utile.
Bernhard Barker
4
L'exemple @DoctorT peut être correct en supposant que l' aopérateur de post-décrémentation est surchargé, ce qui renvoie lvalue. coliru.stacked-crooked.com/a/e1effc351ae79e9f
doc
275

C'est exactement la même chose que

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

pour les nombres non négatifs

Bonne personne
la source
153
Cela ne devrait-il pas être for(--x++;--x;++x--)?
Mateen Ulhaq
9
@DoctorT qui est ce qui unsignedest pour
Cole Johnson
12
@MateenUlhaq, c'est faux selon la norme l'expression --x++a un comportement indéfini selon §1.9.15
WorldSEnder
S'il utilisait unsigned, il aurait utilisé%u
Cacahuete Frito
242

Quoi qu'il en soit, nous avons maintenant un opérateur "va à". "-->"est facile à retenir comme une direction, et "tandis que x va à zéro" est un sens direct.

De plus, il est un peu plus efficace que "for (x = 10; x > 0; x --)"sur certaines plateformes.

Test
la source
17
Va toujours être vrai, surtout lorsque la valeur de x est négative.
Ganesh Gopalasubramanian
15
L'autre version ne fait pas la même chose - avec for (size_t x=10; x-->0; )le corps de la boucle exécuté avec 9,8, .., 0 alors que l'autre version a 10,9, .., 1. Sinon, il est assez difficile de quitter une boucle jusqu'à zéro avec une variable non signée.
Pete Kirkham
4
Je pense que c'est un peu trompeur ... Nous n'avons pas d'opérateur littéralement "va à", car nous avons besoin d'un autre ++>pour faire le travail incrémentiel.
tslmy
19
@Josh: en fait, le débordement donne un comportement indéfini pour int, il pourrait donc tout aussi facilement manger votre chien que de le mettre xà zéro s'il commence au négatif.
SamB
3
C'est un idiome très important pour moi pour la raison donnée dans le comnmet par @PeteKirkham, car j'ai souvent besoin de faire des boucles décroissantes sur des quantités non signées jusqu'à 0. (À titre de comparaison, l'idiome consistant à omettre des tests pour zéro, comme écrire à la while (n--)place pour non signé n, ne vous achète rien et pour moi entrave considérablement la lisibilité.) Il a également la propriété agréable que vous spécifiez un de plus que l'index initial, qui est généralement ce que vous voulez (par exemple, pour une boucle sur un tableau, vous spécifiez sa taille). J'aime aussi -->sans espace, car cela rend l'idiome facile à reconnaître.
Marc van Leeuwen
221

Ce code compare d'abord x et 0, puis décrémente x. (Également dit dans la première réponse: vous post-décrémentez x et comparez ensuite x et 0 avec l' >opérateur.) Voir la sortie de ce code:

9 8 7 6 5 4 3 2 1 0

Nous comparons maintenant d'abord puis décrémentons en voyant 0 dans la sortie.

Si nous voulons d'abord décrémenter puis comparer, utilisez ce code:

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

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

Cette sortie est:

9 8 7 6 5 4 3 2 1
SjB
la source
177

Mon compilateur imprimera 9876543210 lorsque j'exécuterai ce code.

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

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

Comme prévu. Le while( x-- > 0 )signifie en fait while( x > 0). Le x--poste diminue x.

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

est une façon différente d'écrire la même chose.

C'est bien que l'original ressemble à "tandis que x passe à 0".

cool_me5000
la source
4
Le résultat n'est indéfini que lorsque vous incrémentez / décrémentez la même variable plusieurs fois dans la même instruction. Cela ne s'applique pas à cette situation.
Tim Leaf
13
while( x-- > 0 ) actually means while( x > 0)- Je ne suis pas sûr de ce que vous essayez de dire là-bas, mais la façon dont vous l'avez formulé implique que cela --n'a aucun sens, ce qui est évidemment très faux.
Bernhard Barker
Pour ramener le point de départ de @Dukeling, cette réponse n'est pas la même que la publication d'origine. Dans le message d'origine, ce xsera -1après qu'il quitte la boucle, tandis que dans cette réponse, ce xsera 0.
Mark Lakata
148

Il manque un espace entre --et >. xest post-décrémenté, c'est-à-dire décrémenté après avoir vérifié la condition x>0 ?.

M. X
la source
42
L'espace n'est pas manquant - C (++) ignore les espaces blancs.
27
@ H2CO3 Ce n'est pas vrai en général. Il y a des endroits où l'espace blanc doit être utilisé pour séparer les jetons, par exemple dans #define foo()versus #define foo ().
Jens
30
@Jens Et si: "L'espace ne manque pas - C (++) ignore les espaces blancs inutiles."?
Kevin P. Rice
139

--est l' opérateur de décrémentation et >est l' opérateur supérieur à .

Les deux opérateurs sont appliqués comme un seul comme -->.

muntoo
la source
11
Ils sont appliqués comme les 2 opérateurs distincts qu'ils sont. Ils ne sont écrits que de manière trompeuse pour ressembler à "un seul".
underscore_d
129

C'est une combinaison de deux opérateurs. La première --consiste à décrémenter la valeur et >à vérifier si la valeur est supérieure à l'opérande de droite.

#include<stdio.h>

int main()
{
    int x = 10;

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

    return 0;
}

La sortie sera:

9 8 7 6 5 4 3 2 1 0            
Rajeev Das
la source
122

En fait, xest post-décrémentation et avec cette condition est en cours de vérification. Ce n'est pas -->, c'est(x--) > 0

Remarque: la valeur de xest modifiée après la vérification de la condition, car elle post-décrémente. Certains cas similaires peuvent également se produire, par exemple:

-->    x-->0
++>    x++>0
-->=   x-->=0
++>=   x++>=0
AndroidLearner
la source
6
Sauf que ++> peut difficilement être utilisé dans un certain temps (). Un opérateur "va jusqu'à ..." serait ++ <, ce qui ne semble pas aussi agréable. L'opérateur -> est une heureuse coïncidence.
Florian F
2
@BenLeggiero Cela pourrait `` fonctionner '' dans le sens de générer du code qui fait quelque chose (tout en exaspérant les lecteurs qui n'aiment pas le code faux-intelligent), mais la sémantique est différente, car son utilisation de la précrément signifie qu'elle exécutera une itération de moins. Comme exemple artificiel, il n'exécuterait jamais le corps de la boucle s'il était xdémarré à 1, mais le while ( (x--) > 0 )ferait. {edit} Eric Lippert a couvert les deux dans ses notes de version C # 4: blogs.msdn.microsoft.com/ericlippert/2010/04/01/…
underscore_d
120

C et C ++ obéissent à la règle du "grignotage maximal". De la même manière que a --- b est traduit en (a--) - b, dans votre cas, se x-->0traduit par (x--)>0.

Ce que la règle dit essentiellement, c'est que, de gauche à droite, les expressions sont formées en prenant le maximum de caractères qui formeront une expression valide.

Peter Mortensen
la source
5
C'est ce que l'OP a supposé: "((a) ->)" était le grignotage maximal. Il s'avère que l'hypothèse originale de l'OP était incorrecte: "->" n'est pas un opérateur valide maximum.
David
4
Aussi connu comme l'analyse gourmande, si je me souviens bien.
Roy Tinker
1
@RoyTinker Balayage gourmand . L'analyseur n'a rien à voir avec cela.
Marquis de Lorne
27

Pourquoi toutes ces complications?

La réponse simple à la question d'origine est juste:

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

Fait la même chose. Je ne dis pas que vous devriez le faire comme ça, mais cela fait la même chose et aurait répondu à la question dans un message.

C'est x--juste un raccourci pour ce qui précède, et >c'est juste un normal supérieur à operator. Pas de grand mystère!

Il y a trop de gens qui compliquent les choses simples de nos jours;)

Garry_G
la source
17
Cette question n'est pas sur les complications, mais sur ** Caractéristiques cachées et coins sombres de C ++ / STL **
pix
20
Le programme donne ici une sortie différente de l'original car x ici est décrémenté après printf. Cela montre bien comment les «réponses simples» sont généralement incorrectes.
Öö Tiib
2
The OP's way: 9 8 7 6 5 4 3 2 1 0etThe Garry_G way: 10 9 8 7 6 5 4 3 2 1
Anthony
2
Ça ne fait pas la même chose. Déplacez votre x=x-1avant printfalors vous pouvez dire "ça fait la même chose".
CITBL
26

De la manière conventionnelle, nous définirions une condition dans la whileparenthèse de boucle ()et une condition de terminaison à l'intérieur des accolades {}, mais--> définir les deux à la fois.

Par exemple:

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

Cela décrémente aet exécute la boucle tandis que aest supérieur à0 .

Classiquement, ce serait:

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

Dans les deux sens, nous faisons la même chose et atteignons les mêmes objectifs.

Zohaib Ejaz
la source
5
Ceci est une erreur. Le code de la question fait: 'test-write-execute' (testez d'abord, écrivez une nouvelle valeur, exécutez la boucle), votre exemple est 'test-execute-write'.
v010dya
@ v010dya Correction de la réponse, maintenant c'est test-write-executecomme dans la question, merci de l'avoir signalé!
Kotauskas
@VladislavToncharov Votre modification était toujours erronée. Voir le mien.
SS Anne
8

(x --> 0) veux dire (x-- > 0)

  1. vous pouvez utiliser (x -->)
    output -: 9 8 7 6 5 4 3 2 1 0

  2. vous pouvez utiliser (-- x > 0) c'est méchant(--x > 0)
    output -: 9 8 7 6 5 4 3 2 1

  3. vous pouvez utiliser
(--\
    \
     x > 0)

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

  1. vous pouvez utiliser
(\
  \
   x --> 0)

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

  1. vous pouvez utiliser
(\
  \
   x --> 0
          \
           \
            )

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

  1. vous pouvez également utiliser
(
 x 
  --> 
      0
       )

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

de même, vous pouvez essayer de nombreuses méthodes pour exécuter cette commande avec succès

Kalana
la source