En los lenguajes de programación C y C++ , el operador de coma (representado por el token ,
) es un operador binario que evalúa su primer operando y descarta el resultado, y luego evalúa el segundo operando y devuelve este valor (y tipo); hay un punto de secuencia entre estas evaluaciones.
El uso del símbolo de coma como operador es distinto de su uso en llamadas y definiciones de funciones, declaraciones de variables, declaraciones de enumeraciones y construcciones similares, donde actúa como separador .
El operador coma separa expresiones (que tienen valor) de una manera análoga a cómo el punto y coma termina las declaraciones, y las secuencias de expresiones se encierran entre paréntesis de manera análoga a cómo las secuencias de declaraciones se encierran entre llaves: [1] (a, b, c)
es una secuencia de expresiones, separadas por comas, que evalúa la última expresión c
, mientras que {a; b; c;}
es una secuencia de declaraciones y no evalúa ningún valor. Una coma solo puede ocurrir entre dos expresiones (las comas separan expresiones), a diferencia del punto y coma, que ocurre al final de una declaración (no en bloque), los puntos y coma terminan las declaraciones.
El operador coma tiene la precedencia más baja de todos los operadores de C y actúa como un punto de secuencia . En una combinación de comas y punto y coma, los puntos y coma tienen una precedencia menor que las comas, ya que los puntos y coma separan las declaraciones, pero las comas aparecen dentro de las declaraciones, lo que concuerda con su uso como puntuación ordinaria: a, b; c, d
se agrupa como (a, b); (c, d)
porque son dos declaraciones separadas.
El operador de coma ha quedado obsoleto en expresiones con subíndices (a partir de C++20 ); [2] para reducir la confusión y abrir la posibilidad futura de reutilizar la sintaxis para la indexación de matrices multidimensionales. En C++23 , operator[]
se agregó la capacidad de sobrecargar con múltiples argumentos, lo que hace que las expresiones con coma sin paréntesis no se puedan usar en subíndices. [3] El operador de coma aún se puede usar y no está obsoleto en este contexto si la expresión con coma está rodeada por paréntesis (como en a[(b,c)]
).
En este ejemplo, el comportamiento diferente entre la segunda y la tercera línea se debe a que el operador de coma tiene menor precedencia que la asignación. El último ejemplo también es diferente, ya que la expresión de retorno debe evaluarse por completo antes de que la función pueda regresar.
/** * Las comas actúan como separadores en esta línea, no como un operador. * Resultados: a=1, b=2, c=3, i=0 */ int a = 1 , b = 2 , c = 3 , i = 0 ; /** * Asigna el valor de b a i. * Las comas actúan como separadores en la primera línea y como operador en la segunda línea. * Resultados: a=1, b=2, c=3, i=2 */ int a = 1 , b = 2 , c = 3 ; int i = ( a , b ); /** * Asigna el valor de a a i. * Equivalente a: int i = a; int b; * Las comas actúan como separadores en ambas líneas. * Las llaves en la segunda línea evitan la redeclaración de variables en el mismo bloque, * lo que causaría un error de compilación. * La segunda b declarada no recibe ningún valor inicial. * Resultados: a=1, b=2, c=3, i=1 */ int a = 1 , b = 2 , c = 3 ; { int i = a , b ; } /** * Aumenta el valor de a en 2, luego asigna el valor de la operación resultante a + b en i. * Las comas actúan como separadores en la primera línea y como un operador en la segunda línea. * Resultados: a=3, b=2, c=3, i=5 */ int a = 1 , b = 2 , c = 3 ; int i = ( a += 2 , a + b ); /** * Aumenta el valor de a en 2, luego almacena el valor de a en i y descarta los valores no utilizados * de la operación resultante a + b. * Equivalente a: (i = (a += 2)), a + b; * Las comas actúan como separadores en la primera línea y como un operador en la tercera línea. * Resultados: a=3, b=2, c=3, i=3 */ int a = 1 , b = 2 , c = 3 ; int i ; i = a += 2 , a + b ; /** * Asigna el valor de a a i. * Las comas actúan como separadores en ambas líneas. * Las llaves en la segunda línea evitan la redeclaración de variables en el mismo bloque, * lo que causaría un error de compilación. * Las segundas b y c declaradas no reciben ningún valor inicial. * Resultados: a=1, b=2, c=3, i=1 */ int a = 1 , b = 2 , c = 3 ; { int i = a , b , c ; } /** * Las comas actúan como separadores en la primera línea y como operador en la segunda línea. * Asigna el valor de c a i, descartando los valores a y b no utilizados. * Resultados: a=1, b=2, c=3, i=3 */ int a = 1 , b = 2 , c = 3 ; int i = ( a , b , c ); /** * Devuelve 6, no 4, ya que los puntos de la secuencia del operador de coma que siguen a la palabra clave * return se consideran una expresión única que evalúa el valor r de la subexpresión final c=6. * Las comas actúan como operadores en esta línea. */ return a = 4 , b = 5 , c = 6 ; /** * Devuelve 3, no 1, por la misma razón que el ejemplo anterior. * Las comas actúan como operadores en esta línea. */ return 1 , 2 , 3 ; /** * Devuelve 3, no 1, por la misma razón que el ejemplo anterior. Este ejemplo funciona como lo hace * porque return es una palabra clave, no una llamada a una función. Aunque los compiladores * permitirán la construcción return(value), los paréntesis son solo relativos a "value" * y no tienen ningún efecto especial en la palabra clave return. * Return simplemente obtiene una expresión y aquí la expresión es "(1), 2, 3". * Las comas actúan como operadores en esta línea. */ return ( 1 ), 2 , 3 ;
El operador de coma tiene casos de uso relativamente limitados. Debido a que descarta su primer operando, generalmente solo es útil cuando el primer operando tiene efectos secundarios deseables que deben secuenciarse antes del segundo operando. Además, debido a que rara vez se usa fuera de modismos específicos y se confunde fácilmente con otras comas o el punto y coma, es potencialmente confuso y propenso a errores. Sin embargo, hay ciertas circunstancias en las que se usa comúnmente, en particular en bucles for y en SFINAE . [4] Para sistemas integrados que pueden tener capacidades de depuración limitadas, el operador de coma se puede usar en combinación con una macro para anular sin problemas una llamada de función, para insertar código justo antes de la llamada de función.
El uso más común es permitir múltiples sentencias de asignación sin utilizar una sentencia de bloque, principalmente en la inicialización y las expresiones de incremento de un bucle for . Este es el único uso idiomático en la programación básica en C. En el siguiente ejemplo, el orden de los inicializadores del bucle es significativo:
void rev ( char * s , tamaño_t len ) { char * primero ; for ( primero = s , s += len ; s >= primero ; -- s ) { putchar ( * s ); } }
Una solución alternativa a este problema en otros lenguajes es la asignación paralela , que permite que se produzcan múltiples asignaciones dentro de una sola declaración y también utiliza una coma, aunque con una sintaxis y una semántica diferentes. Esto se utiliza en Go en su bucle for análogo. [5]
Fuera de los inicializadores de bucle for (que tienen un uso especial de punto y coma), se puede usar la coma en lugar de un punto y coma, particularmente cuando las instrucciones en cuestión funcionan de manera similar a un incremento de bucle (por ejemplo, al final de un bucle while):
++ p , ++ q ; ++ p ; ++ q ;
La coma se puede utilizar en macros de preprocesador para realizar múltiples operaciones en el espacio de una única expresión sintáctica.
Un uso común es proporcionar mensajes de error personalizados en aserciones fallidas. Esto se hace pasando una lista de expresiones entre paréntesis a la assert
macro, donde la primera expresión es una cadena de error y la segunda expresión es la condición que se está afirmando. La assert
macro muestra su argumento textualmente en caso de un error de aserción. El siguiente es un ejemplo:
#include <stdio.h> #include <assert.h> int main ( void ) { int i ; for ( i = 0 ; i <= 9 ; i ++ ) { assert ( ( "i es demasiado grande!" , i <= 4 ) ); printf ( "i = %i \n " , i ); } return 0 ; }
Producción:
yo = 0yo = 1yo = 2yo = 3yo = 4assert: assert.c:6: test_assert: La afirmación `( "i es demasiado grande!", i <= 4 )' falló.Abortado
Sin embargo, la macro assert generalmente está deshabilitada en el código de producción, así que úsela solo para fines de depuración.
La coma se puede utilizar dentro de una condición (de un if, while, do while o for) para permitir cálculos auxiliares, en particular llamar a una función y utilizar el resultado, con alcance de bloque :
if ( y = f ( x ), y > x ) { ... // declaraciones que involucran x e y }
Existe un modismo similar en Go , donde la sintaxis de la declaración if permite explícitamente una declaración opcional. [6]
La coma se puede utilizar en las instrucciones de retorno para asignar una variable global o un parámetro de salida (pasado por referencia). Este modismo sugiere que las asignaciones son parte del retorno, en lugar de asignaciones auxiliares en un bloque que termina con el retorno real. Por ejemplo, al configurar un número de error global:
si ( error ) retorna ( errno = EINVAL , -1 );
Esto se puede escribir de forma más detallada así:
si ( error ) { errno = EINVAL ; devolver -1 ; }
Para abreviar, se puede utilizar la coma para evitar un bloque y las llaves asociadas, como en:
si ( x == 1 ) y = 2 , z = 3 ;
si ( x == 1 ) y = 2 , z = 3 ;
en lugar de:
si ( x == 1 ) { y = 2 ; z = 3 ;}
si ( x == 1 ) { y = 2 ; z = 3 ; }
En los lenguajes de programación OCaml y Ruby , se utiliza el punto y coma (";") para este propósito. JavaScript [7] y Perl [8] utilizan el operador de coma de la misma manera que C/C++. En Java , la coma es un separador que se utiliza para separar elementos de una lista en varios contextos. [9] No es un operador y no evalúa el último elemento de la lista. [10]
El operando izquierdo siempre se evalúa y todos los efectos secundarios se completan antes de que se evalúe el operando derecho.
Puede utilizar el operador de coma cuando desee incluir varias expresiones en una ubicación que requiera una sola expresión.