¿Qué hace el operador coma ,?

9 minutos de lectura

avatar de usuario
lilq

Lo que hace el , operador hacer en C?

  • posible duplicado de ¿Cuál es el uso correcto del operador coma?

    – Sergey K.

    28 de agosto de 2013 a las 8:00

  • Como señalo en mi respuesta, hay un punto de secuencia después de la evaluación del operando izquierdo. Esto es diferente a la coma en una llamada de función que es solo gramatical.

    – Shafik Yaghmour

    25 de junio de 2014 a las 11:46


  • @SergeyK. — Dado que esto fue preguntado y respondido años antes que el otro, es más probable que el otro sea un duplicado de esta pregunta. Sin embargo, el otro también tiene doble etiqueta con c y c++, lo cual es una molestia. Esta es una sesión de preguntas y respuestas solo para C, con respuestas decentes.

    –Jonathan Leffler

    18 de mayo de 2019 a las 0:18

La expresion:

(expression1,  expression2)

Primero se evalúa expresión1, luego se evalúa expresión2 y se devuelve el valor de expresión2 para toda la expresión.

  • entonces, si escribo i = (5,4,3,2,1,0), idealmente debería devolver 0, ¿correcto? pero se me asigna un valor de 5? ¿Pueden ayudarme a entender dónde me estoy equivocando?

    – Jayesh

    13 de noviembre de 2010 a las 6:55

  • @James: el valor de una operación de coma siempre será el valor de la última expresión. En ningún momento i tienen los valores 5, 4, 3, 2 o 1. Es simplemente 0. Es prácticamente inútil a menos que las expresiones tengan efectos secundarios.

    –Jeff Mercado

    13 de noviembre de 2010 a las 7:06


  • Tenga en cuenta que hay un punto de secuencia completo entre la evaluación del LHS de la expresión de coma y la evaluación del RHS (consulte la respuesta de Shafik Yaghmour para obtener una cita del estándar C99). Esta es una propiedad importante del operador coma.

    –Jonathan Leffler

    20 de junio de 2014 a las 20:47


  • i = b, c; es equivalente a (i = b), c porque debido a la asignación = tiene mayor precedencia que el operador coma ,. El operador coma tiene la precedencia más baja de todos.

    – ciclaminista

    2 de febrero de 2019 a las 6:10

  • Me preocupa que los paréntesis sean engañosos por dos motivos: (1) no son necesarios: el operador de coma no tiene que estar rodeado de paréntesis; y (2) podrían confundirse con los paréntesis alrededor de la lista de argumentos de una llamada de función, pero la coma en la lista de argumentos no es el operador de coma. Sin embargo, arreglarlo no es del todo trivial. Quizás: En la declaración: expression1, expression2; primero expression1 se evalúa, presumiblemente por sus efectos secundarios (como llamar a una función), luego hay un punto de secuencia, luego expression2 se evalúa y el valor devuelto…

    –Jonathan Leffler

    18 de mayo de 2019 a las 0:22

avatar de usuario
accidente

he visto usado más en while bucles:

string s;
while(read_string(s), s.len() > 5)
{
   //do something
}

Hará la operación, luego hará una prueba basada en un efecto secundario. La otra forma sería hacerlo así:

string s;
read_string(s);
while(s.len() > 5)
{
   //do something
   read_string(s);
}

  • ¡Oye, eso es ingenioso! A menudo he tenido que hacer cosas poco ortodoxas en un bucle para solucionar ese problema.

    – staticsan

    4 de septiembre de 2009 a las 1:47

  • Aunque probablemente sería menos oscuro y más legible si hicieras algo como: while (read_string(s) && s.len() > 5). Obviamente eso no funcionaría si read_string no tiene un valor de retorno (o no tiene uno significativo). (Editar: lo siento, no me di cuenta de la antigüedad de esta publicación).

    – jamesdlin

    25 de marzo de 2010 a las 8:05


  • @staticsan No tengas miedo de usar while (1) con un break; declaración en el cuerpo. Intentar forzar la parte de ruptura del código hacia arriba en la prueba while o hacia abajo en la prueba do-while, a menudo es una pérdida de energía y hace que el código sea más difícil de entender.

    – Potrzebie

    27 de septiembre de 2012 a las 6:20

  • @jamesdlin… y la gente todavía lo lee. Si tienes algo útil que decir, entonces dilo. Los foros tienen problemas con los hilos resucitados porque los hilos generalmente se ordenan por fecha de la última publicación. StackOverflow no tiene tales problemas.

    – Dimitar Slavchev

    29 de noviembre de 2012 a las 13:36

  • @potrzebie Me gusta el enfoque de coma mucho mejor que while(1) y break;

    – Miguel

    11 de marzo de 2016 a las 14:19

avatar de usuario
Shafik Yaghmour

Él operador de coma evaluará el operando izquierdo, descartará el resultado y luego evaluará el operando derecho y ese será el resultado. Él idiomático El uso como se indica en el enlace es al inicializar las variables utilizadas en un for bucle, y da el siguiente ejemplo:

void rev(char *s, size_t len)
{
  char *first;
  for ( first = s, s += len - 1; s >= first; --s)
      /*^^^^^^^^^^^^^^^^^^^^^^^*/ 
      putchar(*s);
}

De lo contrario no hay muchos estupendo Usos de la operador de comaaunque es fácil abusar para generar código que es difícil de leer y mantener.

Desde el borrador del estándar C99 la gramática es la siguiente:

expression:
  assignment-expression
  expression , assignment-expression

y Párrafo 2 dice:

Él el operando izquierdo de un operador de coma se evalúa como una expresión vacía; hay un punto de secuencia después de su evaluación. Entonces la se evalúa el operando derecho; el resultado tiene su tipo y valor. 97) Si se intenta modificar el resultado de un operador de coma o acceder a él después del siguiente punto de secuencia, el comportamiento no está definido.

Nota al pie 97 dice:

Un operador de coma hace no producir un lvalue.

lo que significa que no puede asignar al resultado de la operador de coma.

Es importante notar que el operador coma tiene el precedencia más baja y por lo tanto hay casos en los que se utiliza () puede hacer una gran diferencia, por ejemplo:

#include <stdio.h>

int main()
{
    int x, y ;

    x = 1, 2 ;
    y = (3,4) ;

    printf( "%d %d\n", x, y ) ;
}

tendrá la siguiente salida:

1 4

avatar de usuario
Pablo Esteban

El operador de coma combina las dos expresiones a cada lado en una sola, evaluándolas en orden de izquierda a derecha. El valor del lado derecho se devuelve como el valor de la expresión completa.
(expr1, expr2) es como { expr1; expr2; } pero puedes usar el resultado de expr2 en una llamada de función o asignación.

A menudo se ve en for bucles para inicializar o mantener múltiples variables como esta:

for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh)
{
    /* do something with low and high and put new values
       in newlow and newhigh */
}

Aparte de esto, solo lo he usado “con ira” en otro caso, al concluir dos operaciones que siempre deberían ir juntas en una macro. Teníamos un código que copiaba varios valores binarios en un búfer de bytes para enviarlos a una red, y un puntero que se mantenía donde habíamos llegado a:

unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;

*ptr++ = first_byte_value;
*ptr++ = second_byte_value;

send_buff(outbuff, (int)(ptr - outbuff));

donde estaban los valores shorts o ints hicimos esto:

*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;

Más tarde leemos que este C no era realmente válido, porque (short *)ptr ya no es un valor l y no se puede incrementar, aunque a nuestro compilador en ese momento no le importaba. Para arreglar esto, dividimos la expresión en dos:

*(short *)ptr = short_value;
ptr += sizeof(short);

Sin embargo, este enfoque dependía de que todos los desarrolladores recordaran poner ambas declaraciones en todo momento. Queríamos una función en la que pudieras pasar el puntero de salida, el valor y el tipo del valor. Siendo esto C, no C++ con plantillas, no podíamos hacer que una función tomara un tipo arbitrario, así que nos decidimos por una macro:

#define ASSIGN_INCR(p, val, type)  ((*((type) *)(p) = (val)), (p) += sizeof(type))

Al usar el operador de coma, pudimos usarlo en expresiones o declaraciones como deseábamos:

if (need_to_output_short)
    ASSIGN_INCR(ptr, short_value, short);

latest_pos = ASSIGN_INCR(ptr, int_value, int);

send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));

¡No estoy sugiriendo que ninguno de estos ejemplos sea de buen estilo! De hecho, me parece recordar la de Steve McConnell Código completo desaconsejando incluso el uso de operadores de coma en un for bucle: para facilitar la lectura y el mantenimiento, el bucle debe ser controlado por una sola variable, y las expresiones en el for La línea en sí solo debe contener código de control de bucle, no otros bits adicionales de inicialización o mantenimiento de bucle.

Provoca la evaluación de múltiples declaraciones, pero usa solo la última como valor resultante (rvalue, creo).

Asi que…

int f() { return 7; }
int g() { return 8; }

int x = (printf("assigning x"), f(), g() );

debería dar como resultado que x se establezca en 8.

  • Lo hace. Y se establece en 11 si omite las llaves exteriores. Bastante interesante y definitivamente vale la pena una advertencia del compilador para algunos casos.

    – sebkraemer

    12 oct 2020 a las 10:57

avatar de usuario
dgentry

Como han dicho las respuestas anteriores, evalúa todas las declaraciones pero usa la última como el valor de la expresión. Personalmente, solo lo he encontrado útil en expresiones de bucle:

for (tmp=0, i = MAX; i > 0; i--)

  • Lo hace. Y se establece en 11 si omite las llaves exteriores. Bastante interesante y definitivamente vale la pena una advertencia del compilador para algunos casos.

    – sebkraemer

    12 oct 2020 a las 10:57

El único lugar en el que he visto que es útil es cuando escribes un bucle divertido en el que quieres hacer varias cosas en una de las expresiones (probablemente la expresión de inicio o la expresión de bucle). Algo así como:

bool arraysAreMirrored(int a1[], int a2[], size_t size)
{
  size_t i1, i2;
  for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)
  {
    if(a1[i1] != a2[i2])
    {
      return false;
    }
  }

  return true;
}

Perdóneme si hay algún error de sintaxis o si mezclé algo que no sea estrictamente C. No estoy argumentando que el operador , sea una buena forma, pero para eso podría usarlo. En el caso anterior, probablemente usaría un while loop en su lugar para que las múltiples expresiones en init y loop sean más obvias. (E inicializaría i1 e i2 en línea en lugar de declarar y luego inicializar … bla, bla, bla).

  • Supongo que te refieres a i1=0, i2 = tamaño -1

    – Frankster

    13 de agosto de 2009 a las 14:05

¿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