12
votes

int [] arr = {0}; Valeur int = arr [arr [0] ++]; Valeur = 1?

Aujourd'hui, je suis venu une croix un article de Eric Lippert où il essayait de nettoyer le mythe entre la priorité des opérateurs et l'ordre d'évaluation. À la fin, il y avait deux extraits de code qui m'a confondu, voici le premier extrait: XXX Pré>

Maintenant, lorsque je pense à la valeur de la valeur variable, je le calculez simplement à être une. Voici comment je pensais que cela fonctionne.
p>

  1. premier déclarer arrêté comme un tableau d'int avec un article à l'intérieur; ce La valeur de l'élément est 0. li>
  2. seconde obtenir la valeur d'arr [0] --0 dans ce cas. li>
  3. troisième obtenez la valeur de l'arr [la valeur de l'étape 2] (qui est toujours 0) --gets ARR [0] à nouveau à 0. li>
  4. quatrième assignez la valeur de l'étape 3 (0) à la valeur variable. --Value = 0 maintenant li>
  5. Ajouter à la valeur de l'étape 2 1 --NOW arr [0] = 1. Li> ol>

    apparemment c'est faux. J'ai essayé de rechercher les spécifications C # pour une déclaration explicite sur lorsque l'incrément se produit réellement, mais n'a pas trouvé. de
    Le deuxième extrait est d'un commentaire de Eric's Href = "http://blogs.msdn.com/ericlippert/archive/2009/08/10/PRECEDENCE-VS-ORD-REDux.aspx" rel = "NOFOOL NOREFERRER" > Blog POST sur le sujet: P>

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


3 commentaires

Votre cinquième étape n'est pas correcte de toute façon. "Ajouter à la valeur de l'étape 2 1 --NOWR arr [0] = 1." La valeur prise / copiée de la matrice est incrémentée avec une, mais la valeur dans la matrice n'est pas touchée. Donc l'instruction arr [0] = 1 est false. La valeur copiée de l'arr [0] = 1 à l'étape 5.


Eh bien, je viens de courir le code et j'ai eu la valeur 0 pour la 1ère réponse et {11,27,33} Comme le second ... est-ce que ce compilateur est spécifique?


@Swabha vous êtes sûr que vous utilisez c #?


6 Réponses :


19
votes

L'opération postincrimentation se produit dans le cadre d'évaluer l'expression globale. C'est un effet secondaire qui se produit après l'évaluation de la valeur mais avant que toutes les autres expressions soient évaluées.

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

c'est toutes les parties de l'évaluation E ++, avant toute autre chose n'est évaluée.

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


En outre, pour les opérations d'affectation dans lesquelles la LHS est classée comme variable (comme dans ce cas), le LHS est évalué avant le RHS est évalué.

donc dans votre exemple: xxx

est équivalent à: xxx

le bit correspondant de la spécification Pour cela, la section 7.16.1 (spécifique C # 3.0).


14 commentaires

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


@Lbushkin: Je pense que je montais exactement ce but pendant que vous commenciez :)


@Lbushkin faux. Microsoft dit explicitement que la mission (=) et le ternaire (? :) Les opérateurs sont résolus de droit à gauche.


"L'opération postincrimentation se produit immédiatement après l'évaluation de l'expression postincrissante est évaluée" J'aimerais le mettre de cette façon: l'expression postincration a une valeur comme toutes les expressions. Il a également un effet secondaire (contrairement à la plupart des expressions triviales). Vous pouvez y penser comme quelque chose comme un appel de méthode. La valeur qu'une méthode renvoie est totalement indépendante des effets secondaires qu'il pourrait avoir. Fondamentalement, au moment de l'évaluation, une opération postincrimentation effectue un effet secondaire tout en retournant la valeur initiale de l'opérande comme valeur.


@Novash: Comment les opérateurs ternaires pourraient-ils être résolus de droit à gauche? L'expression la plus gauche doit être résolue avant de décider que les deux autres expressions seront résolues (l'autre expression n'est même pas exécutée).


La section 7.16.1 traite des types booléens. Vous n'avez pas voulu dire 7.13.1, qui traite de la mission? De plus, la section 7.5.9 qui traite de Post ++ Spécifie que la valeur n'est modifiée qu'après que l'appel fixé se produit.


@JPBOCHI Il est considéré comme associatif juste quand il est empilé.


@Leahn: Je pense que vous devez regarder une version différente de la spécification pour moi. Je regarde la spécification C # 3.0, où 7.16.1 est très affectant ... et le LHS de l'affectation est évalué en premier.


@Mehrdad: Je vais éditerai un peu, mais pas tout à fait dans cette mesure. @Leahn: la version de la spécification C # que vous avez liée est définitivement ancienne - elle ne couvre pas les expressions de la requête, par exemple.


@Jon Skeet: Je blâme Microsoft. :) Cependant, comme je l'ai dit dans un autre poste, je suis corrigé. J'ai mal compris ce que j'ai lu. Les commandes de résolution correcte sont les suivantes: i = 1, trouvez l'adresse des données [1], ajoutez 1 à i, trouvez l'adresse des données [2], ajoutez 5 à la valeur des données [2], attribuez le résultat des données [1]


@Jon Skeet, donc pour le premier extrait, il va déréférenference Arr [0] puis stocker sa valeur quelque part, puis incrémenter arr [0]. Il utiliserait ensuite la valeur de la température stockée pour évaluer l'expression (l'arrondi externe [0]) qui récupérera la valeur incrémentée. À droite, ou je manque quelque chose d'autre?


@ 7ALWAGY: Oui, c'est vrai (en supposant que je vous ai suivi correctement!)


@Jon: Il ne fait aucun doute que vous avez raison. Mais pourquoi dans le monde, quelqu'un devrait écrire du code comme celui-ci? Où est l'idée de la lisibilité ou devrais-je dire la compréhensibilité?


@shahkalpeh: Évidemment, ces exemples eux-mêmes sont hideusement illisibles - mais je soupçonne qu'il existe d'autres cas qui semblent beaucoup plus lisibles, mais nécessitent les mêmes détails pour être entièrement compris. Ces exemples sont bons pour démontrer l'ordre d'évaluation.



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

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

3 commentaires

Je ne suis pas d'accord avec vous. La mise en œuvre indique que l'opérateur d'affectation est la priorité la plus basse et résolue de droite à gauche. Le gars est correct sur ses hypothèses, selon les spécifications de la langue. msdn.microsoft.com/en-us/library/aa691323 (Vs.71) .aspx


@Leahn Novash: Vous êtes à nouveau confuses associativité avec l'ordre d'évaluation. "A = B = C" est juste associatif "A = (B = C)", mais qui ne dit rien sur l'ordre d'évaluation. L'ordre d'évaluation en C # est toujours laissé à droite.


J'ai encore lu les spécifications plus attentivement. Je me suis trompé.



-4
votes

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 lorsque le compilateur est faux.

Je n'ai aucun 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.


3 commentaires

I ++ et ++ I sont différents et sont utilisés de différentes manières. Tout compilateur qui convertit I ++ vers ++, j'inviterais la colère de nombreux développeurs.


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


Je conviens que les optimisations de compilateur, de gitter ou de processeur peuvent avoir lieu, mais cela se produira si seuls les résultats des optimisations sont indiscernables du résultat souhaité sur une application à une seule application: blogs.msdn.com/ericlippert/archive/2009/08/10/...



0
votes

Je m'attendrais à ce que l'opérateur post-incrément augmente la variable après l'utilisation de sa valeur. 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 P>

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


0 commentaires

5
votes

Pour le premier extrait, la séquence est la suivante:

  1. Déclarez ARR comme vous avez décrit:
  2. récupérez la valeur d'arr [0], qui est 0
  3. incrémentez la valeur d'arr [0] à 1.
  4. Récupérez la valeur de l'ARR [(résultat du n ° 2)] qui est arrête [0], qui (par n ° 3) est de 1.
  5. Store qui aboutit à Valeur .
  6. valeur = 1

    Pour le deuxième 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. Que ajoute-t-on? données [i] + 5, qui est maintenant des données [2] + 5, qui est 38.

      La pièce manquante est que "post" ne signifie pas "après tout le reste". Cela signifie simplement "immédiatement après que je récupère la valeur actuelle de cette variable". Un événement post-incrément qui se produit "au milieu de" une ligne de code est complètement normal.


0 commentaires

0
votes

Vous devez penser aux assignations en trois étapes:

  1. Évaluez le côté gauche (= Obtenir l'adresse où la valeur doit être stockée)
  2. Évaluez le côté droit
  3. Attribuez la valeur à partir de l'étape 2 à l'emplacement de la mémoire à partir de l'étape 1.

    si vous avez quelque chose comme xxx

    alors a () fonctionnera en premier, alors c () s'exécutera, puis le setter de la propriété B sera exécuté. .

    essentiellement, vous devez penser à votre relevé comme xxx


0 commentaires