web-dev-qa-db-fra.com

Compilateurs et arguments d'évaluation de l'évaluation en C ++

D'accord, je suis conscient que la norme dicte que la mise en œuvre de C++ peut choisir dans laquelle les arguments de commande d'une fonction sont évalués, mais y a-t-il des implémentations qui "en profitent" dans un scénario où elle affecterait réellement le programme?

Exemple classique:

int i = 0;
foo(i++, i++);

Remarque: Je ne cherche pas quelqu'un pour me dire que l'ordre d'évaluation ne peut pas être invoqué, j'en connais bien cela. Je ne suis intéressé que si les compilateurs évaluent réellement d'un ordre de gauche à droite, car mon hypothèse serait que s'ils avaient que beaucoup de code mal écrit se cassaient (à juste titre, mais ils se plaignraient toujours).

57
RaptorFactor

Cela dépend du type d'argument, de la convention d'appel de la fonction appelée, de l'archectueuse et du compilateur. Sur un X86, la convention appelle Pascal évalue les arguments laissés à droite alors que dans la Convention C appelant C ( __ CDECL ), il convient de rester à gauche. La plupart des programmes qui fonctionnent sur plusieurs plates-formes prennent en compte les conventions appelantes pour sauter des surprises.

Il y a un bon article sur le blog de Raymond Chen si vous êtes intéressé. Vous voudrez peut-être aussi jeter un coup d'œil à la section pile et appel section du manuel de la GCC.

Edit: Tant que nous fractionnons les cheveux: ma réponse traite cette question non comme une question de langue, mais comme une plate-forme une. La norme de langue ne guranee ou ne préfère pas l'un sur l'autre et la laisse comme non spécifiée . Notez le libellé. Cela ne dit pas que cela est indéfini. Non précisé dans ce sens signifie quelque chose que vous ne pouvez pas compter sur un comportement non portable. Je n'ai pas le C Spec/Draft Handy, mais cela devrait être similaire à celui de mon projet N2798 (C++)

Certains autres aspects et opérations de la machine abstraite sont décrits dans la présente Norme internationale comme non spécifié (par exemple, ordre d'évaluation des arguments à une fonction). Dans la mesure du possible, cette norme internationale définit un ensemble de comportements admissibles. Celles-ci définissent les aspects non déterministes de la machine abstraite. Une instance de la machine abstraite peut donc avoir plus d'une séquence d'exécution possible pour un programme donné et une entrée donnée.

51
dirkgently

J'ai trouvé la réponse dans normes C++ .

Paragraphe 5.2.2.8:

L'ordre d'évaluation des arguments n'est pas spécifié. Tous les effets secondaires des évaluations d'expression des arguments prennent effet avant la saisie de la fonction. L'ordre d'évaluation de l'expression postfix et de la liste d'expressions d'arguments est indéterminé.

En d'autres termes, cela dépend du compilateur uniquement.

7
Igor

Lisez ceci

Ce n'est pas une copie exacte de votre question, mais ma réponse (et quelques autres) couvre également votre question.

Il existe de très bonnes raisons d'optimisation pour lesquelles le compilateur pourrait ne pas choisir simplement à gauche mais aussi les interlaisser.

La norme ne garantit même pas un ordre séquentiel. Il uniquement garantit que lorsque la fonction est appelée, tous les arguments ont été entièrement évalués.

Et oui, j'ai vu quelques versions de GCC faire exactement cela. Pour votre exemple, FOO (0,0) serait appelé, et je serais 2 après. (Je ne peux pas vous donner le numéro de version exact du compilateur. Il y a quelque temps - mais je ne serais pas surpris de voir ce comportement apparaître à nouveau. C'est un moyen efficace de planifier des instructions)

6
jalf

Tous les arguments sont évalués. Commande non définie (selon la norme). Mais toutes les implémentations de C/C++ (que je connaisse) évaluent des arguments de fonction de droit à gauche. Edit: Clang est une exception (voir le commentaire ci-dessous).

Je crois que l'ordre d'évaluation de droite à gauche a été très très ancien (depuis les premiers compilateurs C). Certes, la manière dont C++ n'a été inventée et la plupart des implémentations de C++ conserveraient la même ordonnance d'évaluation, car les implémentations précoces C++ ont tout simplement traduite en C.

Il existe des raisons techniques pour évaluer les arguments de la fonction de droite à gauche. Dans les architectures de pile, les arguments sont généralement poussés sur la pile. En C/C++, vous pouvez appeler une fonction avec plus d'arguments que de spécifie, les arguments supplémentaires sont simulés. Si des arguments sont évalués de gauche à droite et poussés de gauche à droite, puis la fente de pile droite sous le pointeur de pile conservera le dernier argument et que la fonction ne permettra pas de passer au décalage d'un argument particulier (Parce que le nombre réel d'arguments poussés dépend de l'appelant).

Dans une commande push à gauche, la fente de pile droite sous le pointeur de la pile conserve toujours le premier argument et la machine suivante contient le deuxième argument, etc. L'argument des compensations sera toujours déterministe pour la fonction (qui peut être écrite et compilé ailleurs dans une bibliothèque, séparément de l'endroit où il est appelé).

Maintenant, la commande push de droite à gauche ne manda pas l'ordre d'évaluation de droite à gauche, mais dans les premiers compilateurs, la mémoire est rare. Dans l'ordre d'évaluation de droite à gauche, la même pile peut être utilisée en place (essentiellement, après avoir évalué l'argument - qui peut être une expression ou un appel Funciton! - La valeur de retour est déjà à la bonne position sur le empiler). Dans l'évaluation de gauche à droite, les valeurs de l'argument doivent être stockées séparément et la repassage de la pile dans l'ordre inverse.

5
Stephen Chung

La dernière fois que j'ai vu les différences entre VS2005 et GCC 3.x sur un matériel X86 en 2007. Donc, c'est (était?) Une situation très probable. Donc, je ne repose plus jamais sur la commande d'évaluation. Peut-être que c'est mieux maintenant.

2
Robert Gould

Je m'attends à ce que la plupart des compilateurs modernes tentent d'interlacer les instructions informatiques des arguments, étant donné qu'elles sont requises par la norme C++ à être indépendante et ne disposant ainsi de toute interdépendance. Faire cela devrait aider à garder une unité d'exécution de la CPU profondément pipeline complète et augmente ainsi le débit. (Au moins, je m'attendrais à ce que un compilateur qui prétend être un compilateur d'optimisation le ferait lorsque des drapeaux d'optimisation sont donnés.)

2
j_random_hacker