Ordre d’évaluation des parameters avant une fonction appelant en C

Peut-on supposer un ordre d’évaluation des parameters de la fonction en l’appelant en C? D’après le programme suivant, il semble qu’il n’y ait pas d’ordre particulier lorsque je l’ai exécuté.

#include  int main() { int a[] = {1, 2, 3}; int * pa; pa = &a[0]; printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa)); /* Result: a[0] = 3 a[1] = 2 a[2] = 2 */ pa = &a[0]; printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(pa),*(++pa)); /* Result: a[0] = 2 a[1] = 2 a[2] = 2 */ pa = &a[0]; printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(++pa), *(pa)); /* a[0] = 2 a[1] = 2 a[2] = 1 */ } 

Non, les parameters de fonction ne sont pas évalués dans un ordre défini en C.

Voir les réponses de Martin York à Quels sont tous les comportements non définis courants que le programmeur c ++ doit connaître? .

L’ordre d’évaluation des arguments de fonction n’est pas spécifié, à partir de C99 §6.5.2.2p10:

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

Un libellé similaire existe en C89.

De plus, vous modifiez plusieurs fois pa sans intervenir des points de séquence qui invoquent un comportement indéfini (l’opérateur de virgule introduit un sharepoint séquence mais les virgules délimitant les arguments de fonction ne le font pas). Si vous montez les avertissements sur votre compilateur, il devrait vous en avertir:

 $ gcc -Wall -W -ansi -pedantic test.c -o test test.c: In function 'main': test.c:9: warning: operation on 'pa' may be undefined test.c:9: warning: operation on 'pa' may be undefined test.c:13: warning: operation on 'pa' may be undefined test.c:13: warning: operation on 'pa' may be undefined test.c:17: warning: operation on 'pa' may be undefined test.c:17: warning: operation on 'pa' may be undefined test.c:20: warning: control reaches end of non-void function 

Juste pour append quelques expériences.
Le code suivant:

 int i=1; printf("%d %d %d\n", i++, i++, i); 

résulte en

2 1 3 – en utilisant g ++ 4.2.1 sous Linux.i686
1 2 3 – en utilisant SunStudio C ++ 5.9 sous Linux.i686
2 1 3 – en utilisant g ++ 4.2.1 sur SunOS.x86pc
1 2 3 – en utilisant SunStudio C ++ 5.9 sur SunOS.x86pc
1 2 3 – en utilisant g ++ 4.2.1 sur SunOS.sun4u
1 2 3 – en utilisant SunStudio C ++ 5.9 sur SunOS.sun4u

Peut-on supposer un ordre d’évaluation des parameters de la fonction en l’appelant en C?

Non, on ne peut pas supposer que si c’est un comportement non spécifié , le projet de norme C99 de la section 6.5 paragraphe 3 dit:

Le regroupement des opérateurs et des opérandes est indiqué par la syntaxe.74) Sauf spécification ultérieure (pour les opérateurs function-call (), &&, ||,?: Et comma), l’ordre d’évaluation des sous-expressions et l’ordre dans quels effets secondaires ont lieu sont tous deux non spécifiés.

Il est également dit que, sauf spécification ultérieure et en particulier les sites function-call () , nous voyons que plus tard sur le projet de norme dans la section 6.5.2.2 Appels de fonctions, le paragraphe 10 dit:

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

Ce programme présente également un comportement indéfini puisque vous modifiez plusieurs fois pa entre les points de séquence . D’après le projet de section standard 6.5 paragraphe 2 :

Entre le sharepoint séquence précédent et suivant, un object doit avoir sa valeur stockée modifiée au plus une fois par l’évaluation d’une expression. En outre, la valeur antérieure doit être lue uniquement pour déterminer la valeur à stocker.

il cite les exemples de code suivants comme étant indéfinis:

 i = ++i + 1; a[i++] = i; 

Important: notez que, bien que l’ opérateur de virgule introduise des points de séquence, la virgule utilisée dans les appels de fonction est un séparateur et non l’ comma operator . Si nous regardons la section 6.5.17 opérateur de virgule paragraphe 2 dit:

L’opérande gauche d’un opérateur de virgule est évaluée en tant qu’expression vide; il y a un sharepoint séquence après son évaluation.

mais le paragraphe 3 dit:

EXEMPLE Comme indiqué par la syntaxe, l’opérateur de virgule (comme décrit dans ce paragraphe) ne peut pas apparaître dans des contextes où une virgule est utilisée pour séparer des éléments dans une liste (tels que des arguments pour des fonctions ou des listes d’initialiseurs ).

Sans le savoir, avoir des avertissements activés avec gcc utilisant au moins -Wall aurait fourni un message similaire à:

 warning: operation on 'pa' may be undefined [-Wsequence-point] printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa)); ^ 

et par défaut clang avertira avec un message similaire à:

 warning: unsequenced modification and access to 'pa' [-Wunsequenced] printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa)); ~ ^ 

En général, il est important de comprendre comment utiliser vos outils de la manière la plus efficace. Il est important de connaître les indicateurs disponibles pour les avertissements, car vous pouvez trouver cette information ici . Certains drapeaux utiles et qui vous épargneront beaucoup de problèmes à long terme et qui sont communs à gcc et à clang sont -Wextra -Wconversion -pedantic . Pour la compréhension du clang, la profanisation peut être très utile. Par exemple, -fsanitize=undefined intercepte de nombreuses instances de comportement indéfini à l’exécution.

Comme d’autres l’ont déjà dit, l’ordre dans lequel les arguments de fonction sont évalués n’est pas spécifié et il n’y a pas de sharepoint séquence entre leur évaluation. Comme vous changez pa par la suite en passant chaque argument, vous changez et lisez pa deux fois entre deux points de séquence. C’est en fait un comportement indéfini. J’ai trouvé une très bonne explication dans le manuel de GCC, qui pourrait être utile à mon avis:

Les normes C et C ++ définissent l’ordre dans lequel les expressions d’un programme C / C ++ sont évaluées en termes de points de séquence, qui représentent un ordre partiel entre l’exécution de parties du programme: celles exécutées avant le sharepoint séquence et celles exécutées après il. Celles-ci se produisent après l’évaluation d’une expression complète (une qui ne fait pas partie d’une expression plus grande), après l’évaluation du premier opérande d’un &&, ||,? : ou, opérateur (virgule), avant qu’une fonction soit appelée (mais après l’évaluation de ses arguments et l’expression dénotant la fonction appelée), et à certains autres endroits. Sauf indication contraire des règles de points de séquence, l’ordre d’évaluation des sous-expressions d’une expression n’est pas spécifié. Toutes ces règles ne décrivent qu’un ordre partiel plutôt qu’un ordre total, car, par exemple, si deux fonctions sont appelées dans une expression sans sharepoint séquence entre elles, l’ordre d’appel des fonctions n’est pas spécifié. Toutefois, le comité des normes a statué que les appels de fonction ne se chevauchent pas.

Il n’est pas spécifié lorsque des modifications entre les points de séquence des valeurs des objects prennent effet. Les programmes dont le comportement en dépend ont un comportement indéfini; les normes C et C ++ spécifient qu ‘«entre le sharepoint séquence précédent et le point suivant, un object doit avoir sa valeur stockée modifiée au plus une fois par l’évaluation d’une expression. En outre, la valeur antérieure doit être lue uniquement pour déterminer la valeur à stocker. ». Si un programme enfreint ces règles, les résultats d’une implémentation particulière sont totalement imprévisibles.

Les exemples de code avec un comportement non défini sont a = a ++ ;, a [n] = b [n ++] et a [i ++] = i ;. Certains cas plus compliqués ne sont pas diagnostiqués par cette option, et cela peut occasionnellement donner un résultat faussement positif, mais en général, il a été jugé assez efficace pour détecter ce type de problème dans les programmes.

La norme est libellée de manière confuse, il y a donc un débat sur la signification précise des règles de points de séquence dans les cas subtils. Des liens vers les discussions sur le problème, y compris les définitions formelles proposées, peuvent être trouvés sur la page des lectures du CCG, à l’ adresse http://gcc.gnu.org/readings.html .

La modification d’une variable plus d’une fois dans une expression est un comportement non défini. Vous pouvez donc obtenir des résultats différents sur différents compilateurs. Donc, évitez de modifier une variable plus d’une fois.

La réponse de Grant est correcte, elle n’est pas définie.

MAIS,,,

Par votre exemple, votre compilateur semble évaluer dans l’ordre de gauche à droite (sans surprise, l’ordre dans lequel les arguments sont poussés dans la stack). Si vous pouvez faire d’autres tests pour montrer que l’ordre est maintenu de manière cohérente même avec les optimisations activées, et si vous ne vous en tenez qu’à cette version du compilateur, vous pouvez supposer en toute sécurité un ordre de droite à gauche.

C’est totalement non portable et une chose horrible, horrible à faire, cependant.