¿Por qué esta declaración if que combina la asignación y una verificación de igualdad devuelve verdadero?

5 minutos de lectura

avatar de usuario
TehMattGR

He estado pensando en algunos errores de principiante y terminé con el de la if declaración. Expandí un poco el código a esto:

int i = 0;
if (i = 1 && i == 0) {
    std::cout << i;
}

he visto que el if declaración devuelve verdadero, y cout‘s i como 1. Si i es asignado 1 en la instrucción if, ¿por qué i == 0 devolver true?

  • Chicos, esta no es una pregunta de error tipográfico. El OP quiere saber por qué se ingresa la declaración if con este código ya que i se establece en 1.

    – NathanOliver

    22 mayo 2019 a las 20:40

  • ¿O asigna el resultado de 1 && i == 0?

    – JVApen

    22 mayo 2019 a las 20:43

  • Sugerencia para principiantes: no deben usar una construcción de lenguaje tan “avanzada”. Simplemente asigne la variable por separado. Eso también evitará posibles problemas con el punto de secuencia. Este tipo de código en el código práctico generalmente también se verá mal.

    – usuario202729

    23 de mayo de 2019 a las 13:36

  • esto seguramente terminará en una pregunta de entrevista

    – RAZ_Muh_Taz

    23 mayo 2019 a las 22:13

avatar de usuario
NathanOliver

esto tiene que ver con precedencia del operador.

if (i = 1 && i == 0)

no es

if ((i = 1) && (i == 0))

porque ambos && y == tienen mayor precedencia que =. Lo que realmente funciona es

if (i = (1 && (i == 0)))

que asigna el resultado de 1 && (i == 0) a i. Así que si i empieza a 0 después i == 0 es trueasi que 1 && true es true (o 1), y entonces i se pone a 1. Entonces desde 1 es cierto, ingresa el bloque if e imprime el valor que asignó a i.

  • @JörgWMittag Eso es genial. Me gusta que te obliga a usar paréntesis.

    – NathanOliver

    24 de mayo de 2019 a las 12:33

  • Básicamente, i = !i; if (i) correctamente escrito

    – alx

    24 de mayo de 2019 a las 13:11

  • @NathanOliver: Fortress era un lenguaje genial que acertaba en muchas cosas. (El diseñador principal fue Guy L. Steele, por lo que no fue una sorpresa). Desafortunadamente, no fue elegido para la ronda final de financiación de DARPA y, posteriormente, Oracle lo suspendió.

    – Jörg W. Mittag

    24 de mayo de 2019 a las 13:26

  • Naturalmente, pedir un mínimo de advertencias al compilador habría detectado ese error,

    – Deduplicador

    24 de mayo de 2019 a las 14:22

  • Cualquier idioma que no asuma que los enteros y los booleanos son equivalentes también lo detectaría.

    – rghome

    24 de mayo de 2019 a las 14:58

Suponiendo que su código realmente se vea así:

#include <iostream>
using namespace std;

int main()  {
    int i = 0;
    if (i = 1 && i == 0) {
        cout << i;
    }
}

Luego esto:

if (i = 1 && i == 0) {

evalúa como

 if (i = (1 && i == 0)) {

y entonces i se establece en 1.

  • ¿Era realmente necesario el código extra? Parece bastante obvio que este sería el caso, ya que no funcionaría de otra manera.

    – Ilustre Magenta

    23 de mayo de 2019 a las 8:09

  • No solo código adicional innecesario. La respuesta no explica claramente la precedencia del operador.

    – Francisco Zarabozo

    23 de mayo de 2019 a las 8:35

  • Ya que estamos en el tren quisquilloso… Veo un using namespace std!

    –Mateen Ulhaq

    23 de mayo de 2019 a las 10:15

  • Hay un código adicional, pero todavía no es un código incorrecto. Y la respuesta sigue siendo correcta. Por supuesto, no explica la precedencia del operador. ¡Pero alguien podría sugerir que se agregue, en lugar de votar negativamente!

    – B Carlos H

    23 mayo 2019 a las 14:20

  • Wow, -4 es duro, considerando que esto responde la pregunta correctamente, aunque tal vez no de manera óptima. No amplía la precedencia del operador tanto como la otra respuesta, pero dice lo suficiente al respecto en el contexto del código para que cualquiera que pensara eso = vino antes && puede ver el problema. Además, sí, la expansión es superflua, pero no creo que importe tanto. No puedo creer que diferencias tan pequeñas hagan que la gente vote 151 a -4.

    – JoL

    23 mayo 2019 a las 15:21


avatar de usuario
ar18

La respuesta real es:

  1. El compilador da precedencia a “i == 0”, que se evalúa como verdadero.
  2. Luego evaluará i=1 como VERDADERO o FALSO, y dado que los operadores de asignación compilados nunca fallan (de lo contrario, no compilarían), también se evalúa como verdadero.
  3. Dado que ambas declaraciones se evalúan como verdaderas, y VERDADERO && VERDADERO se evalúa como VERDADERO, la declaración si se evaluará como VERDADERO.

Como prueba, solo mire la salida asm de su compilador para el código que ingresó (todos los comentarios son míos):

mov     dword ptr [rbp - 8], 0    ; i = 0;
cmp     dword ptr [rbp - 8], 0    ; i == 0?
sete    al                        ; TRUE (=1)
mov     cl, al
and     cl, 1                     ; = operator always TRUE
movzx   edx, cl
mov     dword ptr [rbp - 8], edx  ; set i=TRUE;
test    al, 1                     ; al never changed,
                                  ; so final ans is TRUE

La salida de asm anterior era de CLANG, pero todos los demás compiladores que miré dieron una salida similar. Esto es cierto para todos los compiladores en ese sitio, ya sean compiladores puros de C o C++, todo sin pragmas para cambiar el modo del compilador (que por defecto es C++ para los compiladores de C++)

Tenga en cuenta que su compilador en realidad no estableció i = 1, sino i = TRUE (lo que significa cualquier valor entero de 32 bits que no sea cero). Esto se debe a que el operador && solo evalúa si una declaración es VERDADERA o FALSA, y luego establece los resultados de acuerdo con ese resultado. Como prueba, intente cambiar i=1 a i=2 y podrá observar por sí mismo que nada cambiará. Compruébelo usted mismo usando cualquier compilador en línea en Explorador del compilador

  • 1) Los documentos se vinculan a la precedencia del operador C, cuando esta pregunta está etiquetada con C++. Dos idiomas diferentes. 2a) i = 1 es una tarea [not an equivalence] operador; 2b) Te puedo asegurar que if (i = 0) se evaluará como una condición falsa tanto en C como en C++, por lo que si se evalúa como verdadero o “nunca falla” es algo engañoso.

    – TrebledJ

    29 de mayo de 2019 a las 5:11

  • and cl, 1 ; = operator always TRUE << corríjame si me equivoco, pero no veo ninguna asignación aquí. representa el 1 && parte de la expresión. Así que esta respuesta básicamente se evalúa como false.

    – enfermo

    29 de mayo de 2019 a las 17:04


  • “Los documentos se vinculan con la precedencia del operador C, cuando esta pregunta está etiquetada con C ++. Dos idiomas diferentes”, y cuando compara la precedencia del operador C con C ++, ¿cuál es la diferencia entre los dos? Tienen la misma precedencia con respecto a este tema, lo cual no es sorprendente, dado que C++ es un derivado directo de C (u otra forma de decirlo es que C es un subconjunto del lenguaje C++, por lo que, por supuesto, tendrán mucho en común, incluida la precedencia). Corregiré mi publicación de todos modos, en caso de que sea confuso.

    – ar18

    30 de mayo de 2019 a las 14:53

  • “Corrígeme si me equivoco, pero no veo ninguna asignación aquí” — ¡Entonces déjame corregirte! El 1 es un valor inmediato y no el resultado de ninguna prueba o cálculo. Es lo que se denomina un valor “presunto VERDADERO”. La única prueba que se realiza es para la declaración i==0, es decir — “cmp dword ptr [rbp – 8]0”. Solo estaría en lo correcto si hubiera dicho “movzx edx,1”. De acuerdo con TODAS las publicaciones anteriores a la mía, debería haber dos comparaciones, pero en la vida real solo hay una, y la salida asm de CADA principal el compilador prueba que esas publicaciones son completamente incorrectas.

    – ar18

    30 mayo 2019 a las 23:43

  • Además de obtener una precedencia incorrecta (consulte la respuesta de NathanOliver para el análisis correcto), hace la afirmación falsa de que el operador de asignación siempre se evalúa como VERDADERO. Probar if ( i = 0 ) { print something }. También tu respuesta se contradice; al principio dices eso i=1 se evalúa antes && se aplica, y luego al final dices que i se establece en el resultado de la && operador.

    –MM

    31 de mayo de 2019 a las 0:02


Tiene que ver con analizar las reglas de derecha a izquierda. Por ejemplo, y = x+5.
Todas las subexpresiones se ponderan en importancia. Dos expresiones de igual importancia se evalúan de derecha a izquierda, . El lado de la expresión && se hace primero, seguido por el LHS.

Tiene sentido para mi.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad