web-dev-qa-db-fra.com

Quel est le résultat de i == (i = 2)?

Exécutez le code suivant:

// In Java, output #####
public static void main(String[] args) {
    int i = 1;

    if(i == (i = 2)) {
        System.out.println("@@@@@");
    } else {
        System.out.println("#####");
    }
}

Mais:

// In C, output @@@@@,I did test on Clion(GCC 7.3) and Visual Studio 2017
int main(int argc, char *argv[]) {
    int i = 1;

    if(i == (i = 2)) {
        printf("@@@@@");
    } else {
        printf("#####");
    }

    return 0;
}

La motivation pour poser cette question provient du code suivant:

// The code is from the JDK 11 - Java.util.concurrent.atomic.AtomicInteger
// I am curious about the behavior of the variable prev.
public final int getAndUpdate(IntUnaryOperator updateFunction) {
    int prev = get(), next = 0;
    for (boolean haveNext = false;;) {
        if (!haveNext)
            next = updateFunction.applyAsInt(prev);
        if (weakCompareAndSetVolatile(prev, next))
            return prev;
        haveNext = (prev == (prev = get()));
    }
}

Alors, comment expliquer les deux modes d’exécution ci-dessus?

44
kangjianwei

Le comportement d'un programme C qui exécute l'expression i == (i = 2) est non défini.

Il vient de C11 6.5p22 :

  1. Si un effet secondaire sur un objet scalaire n'est pas séquencé par rapport à un effet secondaire différent sur le même objet scalaire ou à un calcul de valeur utilisant la valeur du même objet scalaire, le comportement est indéfini. S'il existe plusieurs classements autorisés des sous-expressions d'une expression, le comportement n'est pas défini si un tel effet secondaire non séquencé se produit dans l'un des classements.84)

Le i du côté gauche de == est un calcul de valeur sur la valeur de l'objet scalaire i et du côté droit i = 2 a pour effet secondaire d’attribuer la valeur 2 à i. Les LHS et RHS de == ne sont pas séquencés w.r.t. L'une et l'autre. C'est pourquoi tout le programme n'a pas de sens en C.

Compiler avec gcc -Wall et GCC vont recracher:

unsequenced.c:5:16: warning: operation on ‘i’ may be undefined [-Wsequence-point]
     if(i == (i = 2)) {
             ~~~^~~~

Contrairement à C, Java garantit le ordre d'évaluation pour les opérandes (de gauche à droite), donc

haveNext = (prev == (prev = get()));

est correct en Java. La valeur de LHS est déterminée strictement avant l'évaluation de l'effet secondaire sur le RHS.

En C vous devez pour écrire ceci comme quelque chose comme

newPrev = get();
haveNext = (prev == newPrev);
prev = newPrev;
60
Antti Haapala

Le spécification de langage Java (§15.7) indique:

Le langage de programmation Java garantit que les opérandes des opérateurs semblent être évalués dans un ordre d'évaluation spécifique , à savoir: de gauche à droite.

Le spécification (§15.21.1) indique également que:

La valeur produite par le == l'opérateur est true si la valeur de l'opérande de gauche est égale à la valeur de l'opérande de droite; sinon, le résultat est false.

Par conséquent, en Java, l’instruction if au moment de l’exécution ressemblerait à ce qui suit, qui a évidemment pour résultat false:

if (1 == 2) {

}

En C, il est simplement indéfini (voir réponse d'Antti ).

16
Jacob G.

En C, le comportement de i == (i = 2) n’est pas défini car il tente à la fois de mettre à jour un objet et d’utiliser sa valeur dans un calcul sans point de séquence intermédiaire. Le résultat varie en fonction du compilateur, des paramètres du compilateur et même du code environnant.

5
John Bode