¿Qué hace el ,
operador en C?
¿Qué hace el ,
operador en C?
Respuestas:
La expresion:
(expression1, expression2)
Primero se evalúa la expresión1, luego se evalúa la expresión2 y se devuelve el valor de la expresión2 para toda la expresión.
i
los valores 5, 4, 3, 2 o 1. Es simplemente 0. Es prácticamente inútil a menos que las expresiones tengan efectos secundarios.
i = b, c;
es equivalente a (i = b), c
porque porque la asignación =
tiene mayor prioridad que el operador de coma ,
. El operador de coma tiene la precedencia más baja de todas.
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 se devuelve el valor ...
He visto más utilizado 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);
}
while (read_string(s) && s.len() > 5)
. Obviamente, eso no funcionaría si read_string
no tiene un valor de retorno (o no tiene un valor significativo). (Edit: Lo siento, no se dio cuenta qué edad tenía este post.)
while (1)
con una 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 un desperdicio de energía y hace que el código sea más difícil de entender.
while(1)
y break
;
El operador de coma evaluará el operando izquierdo, descartará el resultado y luego evaluará el operando derecho y ese será el resultado. El uso idiomático como se señala 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 usos excelentes del operador de coma , aunque es fácil abusar de generar código que es difícil de leer y mantener.
Del borrador del estándar C99, la gramática es la siguiente:
expression:
assignment-expression
expression , assignment-expression
y el párrafo 2 dice:
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. Luego se evalúa el operando correcto; 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 es indefinido.
La nota a pie de página 97 dice:
Un operador de coma no produce un valor l .
lo que significa que no puede asignar el resultado del operador de coma .
Es importante tener en cuenta que el operador de coma tiene la prioridad más baja y, por lo tanto, hay casos en los que el uso ()
puede marcar 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á el siguiente resultado:
1 4
El operador de coma combina las dos expresiones a cada lado en una sola, y las evalúa en orden de izquierda a derecha. El valor del lado derecho se devuelve como el valor de toda la expresión.
(expr1, expr2)
es como, { expr1; expr2; }
pero puede usar el resultado de expr2
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 "enfadado" en otro caso, al concluir dos operaciones que siempre deben ir juntas en una macro. Teníamos código que copiaba varios valores binarios en un búfer de bytes para enviarlos a una red, y manteníamos un puntero 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 los valores fueron short
s o int
s lo hicimos:
*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;
Más tarde leemos que esto no era realmente válido C, porque (short *)ptr
ya no es un valor l y no se puede incrementar, aunque a nuestro compilador en ese momento no le importó. Para solucionar esto, dividimos la expresión en dos:
*(short *)ptr = short_value;
ptr += sizeof(short);
Sin embargo, este enfoque se basó en que todos los desarrolladores recordaran poner ambas declaraciones todo el tiempo. Queríamos una función en la que pudieras pasar el puntero de salida, el valor y el tipo de valor. Siendo C, no C ++ con plantillas, no podíamos tener una función que tomara un tipo arbitrario, por lo 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 usar esto en expresiones o como 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 tenga buen estilo! De hecho, me parece recordar el Código Completo de Steve McConnell que aconseja incluso no usar operadores de coma en un for
bucle: para facilitar la lectura y la mantenibilidad, el bucle debe estar controlado por una sola variable y las expresiones en la for
línea solo deben 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 (valor, creo).
Entonces...
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.
El único lugar que he visto que es útil es cuando escribes un bucle funky donde quieres hacer varias cosas en una de las expresiones (probablemente la expresión init 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;
}
Disculpe si hay algún error de sintaxis o si mezclé algo que no es estricto C. No estoy argumentando que el operador es una buena forma, pero para eso podría usarlo. En el caso anterior, probablemente usaría un while
bucle en su lugar, por lo que las múltiples expresiones en init y loop serían más obvias. (E inicializaría i1 e i2 en línea en lugar de declarar y luego inicializar ... bla, bla, bla.)
Estoy reviviendo esto simplemente para responder las preguntas de @Rajesh y @JeffMercado, que creo que son muy importantes, ya que este es uno de los principales éxitos del motor de búsqueda.
Tome el siguiente fragmento de código, por ejemplo
int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);
Imprimirá
1 5
El i
caso se maneja como se explica en la mayoría de las respuestas. Todas las expresiones se evalúan en orden de izquierda a derecha, pero solo se asigna la última i
. El resultado de la (
expresión ) is
1`.
El j
caso sigue diferentes reglas de precedencia ya que ,
tiene la precedencia de operador más baja. Debido a esas reglas, el compilador ve asignación de expresión, constante, constante ... . Las expresiones se evalúan nuevamente en orden de izquierda a derecha y sus efectos secundarios permanecen visibles, por lo tanto, j
es el 5
resultado dej = 5
.
Interesantemente, int j = 5,4,3,2,1;
no está permitido por la especificación del idioma. Un inicializador espera una expresión de asignación, por lo ,
que no se permite un operador directo .
Espero que esto ayude.