Comment les arguments sont-ils évalués dans un appel de fonction?

Considérons ce code:

void res(int a,int n) { printf("%d %d, ",a,n); } void main(void) { int i; for(i=0;i<5;i++) res(i++,i); //prints 0 1, 2 3, 4 5 for(i=0;i<5;i++) res(i,i++); //prints 1 0, 3 2, 5 4 } 

En regardant la sortie, il semble que les arguments ne sont pas évalués de droite à gauche à chaque fois. Qu’est-ce qui se passe exactement ici?

L’ordre d’évaluation des arguments dans un appel de fonction n’est pas spécifié. Le compilateur peut les évaluer dans l’ordre de son choix.

À partir de la norme C99 6.5.2.2/10 “Appels de fonction / sémantique”:

L’ordre d’évaluation du désignateur de fonction, des arguments réels et des sous-expressions dans les arguments réels n’est pas spécifié, mais il existe un sharepoint séquence avant l’appel réel.

Si vous devez vous assurer d’un ordre particulier, l’utilisation de temporaires est la solution habituelle:

 int i; for(i=0;i<5;i++) { int tmp = i; int tmp2 = i++; res(tmp2,tmp); } 

Ce qui est encore plus important (car il en résulte un comportement indéfini, et pas seulement indéterminé), c'est qu'il est généralement impossible d'utiliser un opérande pour les opérateurs d'incrémentation / décrémentation plus d'une fois dans une expression. C'est parce que:

Entre le sharepoint séquence précédent et le suivant, la valeur stockée d'un object doit être modifiée au plus une fois par l'évaluation d'une expression. De plus, la valeur antérieure doit être lue uniquement pour déterminer la valeur à stocker. (6.5 / 2 "Expressions")

Selon la norme: L’ordre d’évaluation des arguments n’est pas spécifié. De plus, notez qu’il n’y a pas de points de séquence (sorte de mile-posts) lorsque les arguments sont évalués. Par conséquent, modifier la même variable dans le cadre de l’argument évoque plus d’une fois un comportement indéfini. Ceci est une FAQ 3.2 . Le code que vous avez posté a donc un comportement ambigu.

Il peut être surprenant que la norme ne la spécifie pas: la raison simple étant que cela permet au compilateur d’effectuer certaines optimisations. (Voir la discussion liée à Q2 de GOTW # 56. )

Dans la plupart des implémentations, ceci est déterminé par ce que l’on appelle la convention d’appel. La convention d’appel détermine non seulement l’ordre, mais impose également la responsabilité de nettoyer la stack, que ce soit à l’appelant ou à l’appelé.

Notez également que main retourne toujours un int .