214 votes

La post-incrémentation et la pré-incrémentation dans une boucle 'for' produisent le même résultat

Les boucles suivantes produisent des résultats identiques, même si l'une utilise l'incrémentation et l'autre l'incrémentation.

Voici le code:

 for(i=0; i<5; i++) {
    printf("%d", i);
}

for(i=0; i<5; ++i) {
    printf("%d", i);
}
 

Je reçois le même résultat pour les deux boucles «for». Est-ce que je manque quelque chose?

356voto

danben Points 35312

Après l'évaluation d' i++ ou ++i, la nouvelle valeur de i sera le même dans les deux cas. La différence entre le pré - et post-incrémentation est dans le résultat de l'évaluation de l'expression elle-même.

++i par incréments i et renvoie la nouvelle valeur de i.

i++ renvoie l'ancienne valeur de i, et les augmentations i.

La raison pour laquelle cela n'a pas d'importance dans une boucle for, c'est que le flux de contrôle fonctionne à peu près comme ceci:

  1. test de la condition
  2. si elle est fausse, mettre fin à
  3. si c'est vrai, exécuter le corps
  4. exécuter l'étape d'incrémentation

Parce que (1) et (4) sont découplés, soit pré - ou de post-incrémentation peut être utilisé.

123voto

Jason Points 125291

Eh bien, c'est simple. Le ci-dessus for boucles sont sémantiquement équivalents

int i = 0;
while(i < 5) {
    printf("%d", i);
    i++;
}

et

int i = 0;
while(i < 5) {
    printf("%d", i);
    ++i;
}

Notez que les lignes i++; et ++i; ont la même sémantique DU point DE vue de CE BLOC DE CODE. Ils ont le même effet sur la valeur de i (incrémenter par un) et, par conséquent, ont le même effet sur le comportement de ces boucles.

Notez qu'il y aurait une différence si la boucle a été réécrite comme

int i = 0;
int j = i;
while(j < 5) {
    printf("%d", i);
    j = ++i;
}

int i = 0;
int j = i;
while(j < 5) {
    printf("%d", i);
    j = i++;
}

C'est parce que dans le premier bloc de code j voit la valeur de i après l'incrémentation (i est incrémenté d'abord, ou de pré-incrémenté, d'où le nom) et dans le deuxième bloc de code j voit la valeur de i avant l'augmentation.

102voto

Anders Sjöqvist Points 1358

Le résultat de votre code sera le même. La raison en est que les deux incrémentation opérations peuvent être considérées comme les deux distincts des appels de fonction. Les deux fonctions de provoquer une incrémentation de la variable, et que leurs valeurs de retour sont différents. Dans ce cas, la valeur de retour est tout simplement jetés, ce qui signifie qu'il n'y a pas à distinguer la différence dans la sortie.

Cependant, sous le capot, il y a une différence: La post-incrémentation i++ besoin de créer une variable temporaire pour stocker la valeur d'origine de l' i, puis effectue l'incrémentation et retourne la variable temporaire. La pré-incrémentation ++i ne pas créer une variable temporaire. Bien sûr, tout décent paramètre d'optimisation devrait être en mesure d'optimiser cette distance lorsque l'objet est quelque chose de simple comme un int, mais n'oubliez pas que le ++-opérateurs sont surchargés en plus compliqué classes comme les itérateurs. Depuis les deux méthodes surchargées peuvent avoir différentes opérations (on peut avoir envie de sortie "Hé, je suis pré-incrémenté!" sur la sortie standard par exemple), le compilateur ne peut pas dire si les méthodes sont équivalentes lorsque la valeur de retour n'est pas utilisé (en principe, car un tel compilateur, permettrait de résoudre l'insoluble problème de l'arrêt), il faut utiliser le plus cher de post-incrémentation de la version si vous écrivez myiterator++.

Trois raisons pour lesquelles vous devriez pré-incrémentation:

  1. Vous n'aurez pas à réfléchir pour savoir si la variable/objet pourrait avoir une surcharge de post-incrémentation de la méthode (par exemple, dans un modèle de fonction) et de les traiter de manière différente (ou oublier de les traiter différemment).
  2. Compatible code a l'air mieux.
  3. Quand quelqu'un vous demande "Pourquoi avez-vous pré-incrément?", vous aurez la chance de leur apprendre sur le problème de l'arrêt et des limites théoriques d'optimisation du compilateur. :)

31voto

Adam Liss Points 27815

C'est l'un de mes préférés de questions d'entrevue. Je vais vous expliquer la réponse tout d'abord, et puis vous dire pourquoi j'aime la question.

Solution:

La réponse est que les deux extraits afficher les nombres de 0 à 4 inclusivement. C'est parce qu'un for() boucle est généralement équivalent à un while() boucle:

for (INITIALIZER; CONDITION; OPERATION) {
    do_stuff();
}

Peut être écrite:

INITIALIZER;
while(CONDITION) {
    do_stuff();
    OPERATION;
}

Vous pouvez voir que l'OPÉRATION est toujours fait à la fin de la boucle. Dans ce formulaire, il doit être clair que l' i++ et ++i ont le même effet: ils vont à la fois incrément i et ignorer le résultat. La nouvelle valeur de i n'est pas testé jusqu'à la prochaine itération commence, au sommet de la boucle.


Edit: Merci à Jason pour préciser que ce for() de while() d'équivalence de ne pas tenir si la boucle contient les instructions de contrôle (comme l' continue) qui empêcherait OPERATION d'être exécuté dans un while() boucle. OPERATION est toujours exécutée juste avant la prochaine itération d'un for() boucle.


Pourquoi c'est une Bonne Question d'Entrevue

Tout d'abord, il ne prend qu'une minute ou deux, si un candidat dit de la la bonne réponse immédiatement, afin que nous puissions aller à droite à la prochaine question.

Mais étonnamment (pour moi), de nombreux candidats dites-moi la boucle avec la post-graduation afficher les nombres de 0 à 4, et la pré-incrémentation de la boucle va imprimer de 0 à 5 ou de 1 à 5. Ils expliquent généralement la différence entre le pré - et post-incrémentation correctement, mais ils comprennent mal la mécanique de l' for() boucle.

Dans ce cas, je leur demande de réécrire la boucle à l'aide de while(), et cela me donne vraiment une bonne idée de leur processus de pensée. Et c'est pourquoi je vous pose la question en premier lieu: je veux savoir comment ils abordent un problème, et de la façon de procéder lorsque j'ai jeté un doute sur la façon dont leur monde fonctionne.

À ce stade, la plupart des candidats à réaliser leur erreur et de trouver la bonne réponse. Mais j'en ai eu un qui a insisté pour sa réponse originale à cette question a droite, puis a changé la façon dont il traduit l' for() de la while(). Il a fait une interview passionnante, mais nous n'avons pas à faire une offre!

Espérons que ça aide!

8voto

tvanfosson Points 268301

Parce que dans les deux cas, l'incrément est fait après que le corps de la boucle et donc n'affecte pas les calculs de la boucle. Si le compilateur est bête, c'est peut-être légèrement moins efficace à l'utilisation de post-incrémentation (parce que normalement il doit conserver une copie du pré de la valeur pour les utiliser plus tard), mais je m'attends à des différences pour être optimisé loin dans ce cas.

Il peut être utile de réfléchir à la boucle for est mis en œuvre, essentiellement traduite en un ensemble de devoirs, des tests, et les instructions de branchement. Dans le pseudo-code de la pré-incrémentation ressemblerait à:

      set i = 0
test: if i >= 5 goto done
      call printf,"%d",i
      set i = i + 1
      goto test
done: nop

La Post-incrémentation aurait au moins une autre étape, mais il serait trivial pour optimiser loin

      set i = 0
test: if i >= 5 goto done
      call printf,"%d",i
      set j = i   // store value of i for later increment
      set i = j + 1  // oops, we're incrementing right-away
      goto test
done: nop

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X