Asignación de valor en condición de bucle while

6 minutos de lectura

avatar de usuario
Josip

yo fundar este fragmento de código en Wikipedia.

#include <stdio.h>

int main(void)
{
  int c;

  while (c = getchar(), c != EOF && c != 'x')
  {
    switch (c)
      {
      case '\n':
      case '\r':
        printf ("Newline\n");
        break;
      default:
        printf ("%c",c);
      }
  }
  return 0;
}

Tengo curiosidad acerca de la expresión utilizada como condición para el ciclo while:

while (c = getchar(), c != EOF && c != 'x')

Es bastante obvio lo que hace, pero nunca antes había visto esta construcción. ¿Es esto específico para tiempo ¿círculo? De lo contrario, ¿cómo determina el analizador/compilador qué lado de la expresión separada por comas devuelve el valor booleano para tiempo ¿círculo?

  • No afecta su pregunta, solo el título, pero: c se asigna en el ciclo while, no se inicializa.

    –Steve Jessop

    16 de julio de 2009 a las 8:43

  • Ahí, lo he cambiado. Gracias.

    – Josip

    16 de julio de 2009 a las 10:54

los operador de coma es un operador binario que evalúa su primer operando y descarta el resultado, luego evalúa el segundo operando y devuelve este valor.

también es un “punto de secuencia”lo que significa que todos los efectos secundarios se calcularán antes de que se ejecute la siguiente parte del código.

  • Solo para agregar, porque puede no ser obvio: el efecto del operador “=” que actualiza el valor de la variable c se considera un “efecto secundario” en este contexto.

    – café

    16 de julio de 2009 a las 8:19

  • Correcto, porque el operador de coma tiene la precedencia más baja.

    –Paul Dixon

    16 de julio de 2009 a las 8:45

avatar de usuario
paxdiablo

El operador de coma es una bestia extraña hasta que llegas a entenderlo, y no es específico de while.

La expresion:

exp1, exp2

evalúa exp1 luego evalúa exp2 y regresa exp2.

Lo ves con frecuencia, aunque es posible que no te des cuenta:

for (i = j = 0; i < 100; i++, j += 2)

en realidad no eres usando el valor de retorno de "i++, j += 2" pero está ahí de todos modos. El operador coma evalúa ambos bits para modificar ambos i y j.

Puede usarlo bastante bien en cualquier lugar donde se pueda usar una expresión normal (esa coma dentro de sus llamadas de función es no un operador de coma, por ejemplo) y es muy útil para escribir código fuente compacto, si eso es lo que le gusta. De esa manera, es parte de la familia que permite cosas como:

while ((c= getchar()) != EOF) {...}
i = j = k = 0;

y así.

Para su ejemplo específico:

while (c = getchar(), c != EOF && c != 'x')

ocurre lo siguiente:

  • c = getchar() se ejecuta completamente (el operador de coma es un punto de secuencia).
  • c != EOF && c != 'x' es ejecutado.
  • el operador de coma descarta el primer valor (c) y “devuelve” el segundo.
  • la while utiliza ese valor devuelto para controlar el bucle.

  • Si quisiera encadenar varios operadores de coma en una expresión, ¿sería seguro hacerlo sin paréntesis?

    – Josip

    16 de julio de 2009 a las 8:23

  • Sí, el operador coma tiene la precedencia más baja de todos.

    – café

    16 de julio de 2009 a las 8:33

  • @Josip, tendría cuidado al respecto, el operador de coma tiene una precedencia muy baja que debería hacerlo bien, pero es posible que se encuentre con resultados inesperados. Aunque creo que tu pueden úselo en cualquier lugar donde se permita una expresión, eso no significa necesariamente que usted debería.

    – pax diablo

    16 de julio de 2009 a las 8:34

avatar de usuario
Blixt

En muchos idiomas, la coma es un operador que siempre da como resultado el valor del segundo operando. Los operandos se evalúan secuencialmente de izquierda a derecha.

Pseudocódigo:

a = 10
print a = 7 + 8, a * 2

Nota: print se considera una declaración que no toma argumentos, por lo que lo que viene después se considera la expresión única a = 7 + 8, a * 2.

Ejecutado así:

  • Primera linea
    • poner 10 en a
  • Segunda linea
    • evaluar 7 + 8 (15)
    • poner valor evaluado (15) en a
    • evaluar a * 2 (30)
    • evaluar , operador con operandos 15 y 30:
      • siempre valor del segundo operando (30)
    • imprimir valor evaluado (30)

Para ampliar un poco las otras respuestas, en este código:

EXPRESSION_1 , EXPRESSION_2

Primero se evalúa EXPRESSION_1, luego hay un punto de secuencia, luego se evalúa EXPRESSION_2 y el valor de todo es el valor de EXPRESSION_2.

La garantía de orden de operación y el punto de secuencia son importantes para el código que citó. Juntos, significan que podemos estar seguros de que se llama a la función getchar() y que el valor de la variable c se actualiza por completo antes de que se pruebe el valor de c.

avatar de usuario
mmx

los coma es un operador. Devuelve el valor de la expresión de la derecha. por defecto. Se garantiza que el orden de evaluación será primero a la izquierda y luego a la derecha.

ACTUALIZACIÓN (respuesta al comentario de Pax):

Al igual que la mayoría de los operadores, puede sobrecargarse para los tipos definidos por el usuario:

#include <iostream>
#include <string>
using namespace std;

enum EntryType { Home, Cell, Address };

class AddressBookEntryReference {
public:
    AddressBookEntryReference(const string& name, const EntryType &entry)
        : Name(name), Entry(entry) { }
    string Name;
    EntryType Entry;
};

AddressBookEntryReference operator,(const string& name, const EntryType &type) {
    return AddressBookEntryReference(name, type);
}

class AddressBook {
    string test;
public:
    string& operator[](const AddressBookEntryReference item) {
        // return something based on item.Name and item.Entry.

        // just to test:
        test = item.Name;
        return test;
    }
};

int main() {
    // demo:
    AddressBook book;
    cout << book["Name", Cell]  // cool syntax! 
         << endl;
}

  • Tengo curiosidad por la frase “de forma predeterminada”. No estás sugiriendo que este comportamiento se pueda configurar, ¿verdad? Creo que el estándar deja bastante claro que así es como es, no se permiten desviaciones.

    – pax diablo

    16 de julio de 2009 a las 8:22

  • Mi pregunta original estaba relacionada con el lenguaje C: te has quedado profundamente en las aguas de C ++. En cualquier caso, muy interesante el uso de la sobrecarga.

    – Josip

    16 de julio de 2009 a las 10:58

  • Josip: Oh, no me di cuenta de la etiqueta C. Mi respuesta original es aplicable a ambos, pero mi respuesta al comentario de Pax es definitivamente solo C++.

    – mmx

    16 de julio de 2009 a las 11:02

  • Disculpas, @Mehrdad, no pensé en el ángulo de C++.

    – pax diablo

    16 de julio de 2009 a las 13:56

  • Tengo curiosidad por la frase “de forma predeterminada”. No estás sugiriendo que este comportamiento se pueda configurar, ¿verdad? Creo que el estándar deja bastante claro que así es como es, no se permiten desviaciones.

    – pax diablo

    16 de julio de 2009 a las 8:22

  • Mi pregunta original estaba relacionada con el lenguaje C: te has quedado profundamente en las aguas de C ++. En cualquier caso, muy interesante el uso de la sobrecarga.

    – Josip

    16 de julio de 2009 a las 10:58

  • Josip: Oh, no me di cuenta de la etiqueta C. Mi respuesta original es aplicable a ambos, pero mi respuesta al comentario de Pax es definitivamente solo C++.

    – mmx

    16 de julio de 2009 a las 11:02

  • Disculpas, @Mehrdad, no pensé en el ángulo de C++.

    – pax diablo

    16 de julio de 2009 a las 13:56

¿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