2009-08-11 9 views
12

Aujourd'hui, je suis tombé sur un article de Eric Lippert où il essayait d'effacer le mythe entre la priorité des opérateurs et l'ordre d'évaluation. A la fin, il y avait deux extraits de code qui me suis embrouillé, voici le premier extrait:
int [] arr = {0}; valeur int = arr [arr [0] ++]; Valeur = 1?

 int[] arr = {0}; 
     int value = arr[arr[0]++]; 

Maintenant, quand je pense à la valeur de la variable, je calcule simplement l'un. Voici comment je pensais que ça fonctionnait.

  1. d'abord déclarer arr comme un tableau d'int avec un élément à l'intérieur de celui-ci; cette valeur de l'élément est 0.
  2. Deuxième obtenir la valeur de arr [0] - 0 dans ce cas.
  3. Troisième obtenir la valeur de arr [la valeur de l'étape 2] (qui est encore 0) --gets arr [0] à nouveau --still 0.
  4. quatrième affecter la valeur de l'étape 3 (0) à la valeur de la variable. --value = 0 maintenant
  5. Ajouter à la valeur de l'étape 2 1 --Now arr [0] = 1.

Apparemment, cela est faux. J'ai essayé de rechercher les spécifications C# pour une déclaration explicite sur le moment où l'incrément est en train de se produire, mais je n'en ai trouvé aucun.
Le deuxième extrait est d'un commentaire d'Eric de blog post sur le sujet:

int[] data = { 11, 22, 33 }; 
int i = 1; 
data[i++] = data[i] + 5; 

est maintenant ici que je pense que ce programme exécutera --after déclarant le tableau et l'attribution d'1 à i. [Plz ours avec moi]

  1. Obtenir des données [i] --1
  2. Ajouter à la valeur de l'étape 1 la valeur 5 --6
  3. Assigner aux données [i] (ce qui est encore 1) la valeur de l'étape 2 --data [i] = 6
  4. i Increment - i = 2

Selon ma compréhension, ce tableau doit maintenant contenir les valeurs {11, 27 , 33}. Cependant, quand j'ai bouclé pour imprimer les valeurs de tableau que j'ai: {11, 38, 33}. Cela signifie que l'incrément post s'est produit avant de déréférencer le tableau!
Comment ça se fait? Ce post-incrément n'est-il pas censé être post? C'est-à-dire après tout le reste.
Qu'est-ce qui me manque les gars?

+0

Votre cinquième étape n'est pas correct de toute façon. "Ajouter à la valeur de l'étape 2 1 - Maintenant arr [0] = 1." La valeur qui est prise/copiée du tableau est incrémentée d'un, mais la valeur ** dans ** le tableau n'est pas touchée. Donc, l'instruction arr [0] = 1 est fausse. La valeur copiée de arr [0] = 1 à l'étape 5. – Gertjan

+0

Eh bien, j'ai juste couru le code et j'ai eu la valeur 0 pour la première réponse et {11,27,33} comme seconde ... Est-ce que ce compilateur est spécifique? ? – bubblegum

+1

@Swabha êtes-vous sûr d'utiliser C#? – Galilyou

Répondre

16

L'opération post-incrémentation intervient dans le cadre de l'évaluation de l'expression globale. C'est un effet secondaire qui survient après l'évaluation de la valeur mais avant que toutes les autres expressions ne soient évaluées.

En d'autres termes, pour toute expression E, E ++ (si légal) représente quelque chose comme (pseudo-code):

T tmp = E; 
E += 1; 
return tmp; 

C'est tout partie de l'évaluation E ++, avant toute autre chose est évaluée.

Voir la section 7.5.9 de la spécification C# 3.0 pour plus de détails.


De plus, pour les opérations d'affectation où le LHS est classé comme une variable (comme dans ce cas), le LHS est évalué avant l'ERS est évaluée.

Donc, dans votre exemple:

int[] data = { 11, 22, 33 }; 
int i = 1; 
data[i++] = data[i] + 5; 

équivaut à:

int[] data = { 11, 22, 33 }; 
int i = 1; 
// Work out what the LHS is going to mean... 
int index = i; 
i++; 
// We're going to assign to data[index], i.e. data[1]. Now i=2. 

// Now evaluate the RHS 
int rhs = data[i] + 5; // rhs = data[2] + 5 == 38 

// Now assign: 
data[index] = rhs; 

Le bit correspondant du cahier des charges de cette section est 7.16.1 (spec C# 3.0).

+3

Cela, et les expressions (y compris les affectations) sont évaluées de gauche à droite sauf lorsque la priorité de l'opérateur en impose autrement. Ainsi, les données [i ++] (le côté gauche de l'affectation) sont évaluées avant les données [i] sur le côté droit. – LBushkin

+0

@LBushkin: Je pense que j'étais en train d'éditer pour exactement ce but pendant que vous commentiez :) –

+0

@LBushkin Wrong. Microsoft dit explicitement que les opérateurs affectation (=) et ternaire (? :) sont résolus de droite à gauche. –

2
data[i++] // => data[1], then i is incremented to 2 

data[1] = data[2] + 5 // => 33 + 5 
+0

Je ne suis pas d'accord avec vous. L'implémentation indique que l'opérateur d'affectation est prioritaire et résolu de droite à gauche. Le gars est correct sur ses hypothèses, selon les spécifications linguistiques. http://msdn.microsoft.com/en-us/library/aa691323(VS.71).aspx –

+2

@Leahn Novash: Vous confondez à nouveau l'associativité avec l'ordre d'évaluation. "a = b = c" est juste associatif "a = (b = c)", mais cela ne dit RIEN sur l'ordre d'évaluation. L'ordre d'évaluation en C# est TOUJOURS de gauche à droite. – Daniel

+0

J'ai relu les spécifications plus attentivement. Je me suis trompé. –

-4

La cause peut être que certains compilateurs optimisent i ++ pour être ++ i. La plupart du temps, le résultat final est le même, mais il me semble être l'une de ces rares occasions où le compilateur a tort.

Je n'ai pas encore accès à Visual Studio pour le confirmer, mais essayez de désactiver l'optimisation du code et de voir si les résultats resteront les mêmes.

+4

i ++ et ++ i sont différents et sont utilisés de différentes manières. N'importe quel compilateur qui a converti i ++ en ++ j'inviterait la colère de beaucoup de développeurs. – NickAldwin

+1

Heureusement, C# définit son comportement plutôt mieux que cela. –

+1

Je suis d'accord que des optimisations de compilateur, de gitter ou de processeur peuvent avoir lieu, mais cela se produira si seulement les résultats des optimisations sont indiscernables du résultat souhaité sur une application monothread: http://blogs.msdn.com/ericlippert /archive/2009/08/10/precedence-vs-order-redux.aspx – Galilyou

0

Je m'attendrais à ce que l'opérateur de post-incrémentation incrémente la variable après que sa valeur soit utilisée. Dans ce cas, la variable est incrémentée avant la deuxième référence à la variable.

Si ce ne serait pas, vous pouvez écrire

data[i++] = data[i++] + data[i++] + data[i++] + 5 

Si ce serait comme vous le dites, alors vous pouvez supprimer l'opérateur d'incrémentation, car il ne fait pas vraiment quoi que ce soit, dans l'instruction je l'ai signalé .

5

Pour le premier fragment, dont la séquence est la suivante:

  1. déclarer arr comme vous décrit:
  2. récupérer la valeur d'arr [0], qui est égal à 0
  3. incrémenter la valeur du arr [0 ] à 1.
  4. Récupérer la valeur arr [(résultat # 2)] qui est arr [0], qui (per # 3) est égal à 1.
  5. magasin qui se traduisent par value.
  6. value = 1

Pour le second extrait, l'évaluation est toujours de gauche à droite.

  1. Où stockons-nous le résultat? Dans les données [i ++], qui sont des données [1], mais maintenant i = 2
  2. Qu'ajoutons-nous?data [i] + 5, qui est maintenant data [2] + 5, qui est 38.

La pièce manquante est que "post" ne signifie pas "après TOUT autre chose". Cela signifie simplement "immédiatement après avoir récupéré la valeur actuelle de cette variable". Un post-incrément se produisant "au milieu" d'une ligne de code est tout à fait normal.

0

Vous devez penser des affectations en trois étapes:

  1. Évaluer le côté gauche (= get adresse où la valeur doit être stockée)
  2. Évaluer côté main droite
  3. Affecte la valeur de l'étape 2 à l'emplacement de mémoire à l'étape 1.

Si vous avez quelque chose comme

A().B = C() 

Ensuite, A() s'exécutera en premier, puis C() s'exécutera, puis le paramètre B de la propriété sera exécuté.

Essentiellement, vous devez penser à votre déclaration

StoreInArray(data, i++, data[i] + 5); 
Questions connexes