Menu

Operadores en C: aritméticos, lógicos y precedencia

Todos los operadores de C que vale la pena conocer —aritméticos, de asignación, de comparación, lógicos y de incremento— más la trampa de la división entera, cómo se comporta el módulo con negativos y una tabla de precedencia en la que puedes confiar.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Los operadores son los verbos de C: toman valores y producen otros nuevos. Hay cerca de 40, pero una docena cubre casi todo el código, y dos de ellos tienen trampas lo bastante afiladas como para merecer su propia sección.

Operadores aritméticos

(%% en la cadena de formato imprime un signo de porcentaje literal; un % solo empezaría un especificador de formato.)

Los cinco operadores aritméticos binarios son + - * / %, más el - unario para la negación y el +, que esencialmente no hace nada.

La trampa de la división entera

a / b con dos enteros hace división entera y tira el resto. No hay redondeo: el resultado se trunca hacia cero.

Ese último caso es el error que sobrevive hasta producción. Asignar a un double no ayuda: la división total / count ya está hecha, y ya es un int, antes de que ocurra la asignación. La solución es hacer que un operando sea de punto flotante dentro de la expresión, normalmente con una conversión.

Dos reglas relacionadas: dividir entre cero con enteros es comportamiento indefinido y típicamente revienta el programa, y INT_MIN / -1 se desborda.

El operador módulo

% da el resto y funciona solo con enteros:

Comprobar x % 2 == 0 para la paridad y usar % n para que un índice dé la vuelta en un array son los dos usos cotidianos.

Con números negativos, el % de C sigue el signo del dividendo:

Así que x % 2 == 1 es una prueba rota de imparidad cuando x puede ser negativo: -7 % 2 es -1. Usa x % 2 != 0 en su lugar.

Para restos de punto flotante, % es un error de compilación; usa fmod() de math.h.

Asignación y asignación compuesta

= guarda un valor. Las formas compuestas combinan una operación con el guardado:

x += 5 significa x = x + 5, pero evalúa x una sola vez, lo que importa cuando el destino es algo como arr[compute_index()].

La asignación es en sí una expresión que produce el valor asignado, que es por lo que a = b = c = 0 funciona (asigna de derecha a izquierda) y por lo que el error de = frente a == compila en silencio.

Incremento y decremento

++ suma uno, -- resta uno. Cada uno viene en forma prefija y sufija:

Como sentencia suelta las dos son intercambiables, e i++ es la elección convencional en un encabezado for por ninguna razón más profunda que la tradición.

Lo que no debes hacer es usar una variable más de una vez en una expresión donde además se modifica:

int i = 5;
int x = i++ + i++;      /* comportamiento indefinido */
arr[i] = i++;           /* comportamiento indefinido */
printf("%d %d", i++, i);/* comportamiento indefinido */

C no define el orden en que se evalúan esas subexpresiones, así que el resultado no es meramente no especificado: el comportamiento del programa entero queda indefinido. Parte la sentencia en dos y la ambigüedad desaparece.

Operadores de comparación

Son seis, y todos producen un int: 1 para verdadero y 0 para falso.

Que el resultado sea un int corriente en lugar de un tipo booleano propio es un rasgo definitorio de C; mira booleanos en C para lo que se deriva de ello, incluido el error de = frente a ==.

Dos errores de comparación son específicos de C. El encadenamiento no funciona como sugieren las matemáticas: if (1 < x < 10) siempre es verdadero, porque 1 < x se evalúa a 0 o 1 y luego eso se compara con 10. Escribe if (x > 1 && x < 10). Y comparar cadenas con == compara punteros, no texto: usa strcmp de string.h.

Operadores lógicos

&& es Y, || es O, ! es NO. Los tres tratan cualquier valor distinto de cero como verdadero.

Hacen cortocircuito: && se detiene en cuanto un operando es falso, y || se detiene en cuanto uno es verdadero. El resto nunca se evalúa, y eso no es solo una optimización: es una garantía en la que puedes apoyarte para la seguridad.

Invierte el orden de esas dos pruebas y la llamada con NULL desreferencia un puntero nulo y revienta. Protege primero, usa después: el orden dentro de una cadena && es parte de la lógica.

El operador condicional

El único operador de tres operandos de C elige entre dos valores:

condicion ? valor_si_verdadero : valor_si_falso. Es una expresión, así que cabe donde una sentencia if no puede: dentro de un argumento de printf, en un inicializador. Mantenlo corto; los condicionales anidados se vuelven ilegibles rápido.

Operadores de bits, en breve

Estos trabajan sobre los bits individuales de un entero:

Se usan para banderas, máscaras y registros de hardware. Dos advertencias: & y | no son && ni || (no hacen cortocircuito y operan bit a bit), y desplazar un valor con signo o desplazar más allá del ancho del tipo es indefinido. Usa tipos sin signo para trabajar con bits.

Precedencia

Cuando una expresión no tiene paréntesis, esta tabla decide qué se asocia con qué. La precedencia más alta primero:

NivelOperadoresAsociatividad
1() [] -> . x++ x--de izquierda a derecha
2! ~ ++x --x +x -x *p &x sizeof (tipo)de derecha a izquierda
3* / %de izquierda a derecha
4+ -de izquierda a derecha
5<< >>de izquierda a derecha
6< <= > >=de izquierda a derecha
7== !=de izquierda a derecha
8&de izquierda a derecha
9^de izquierda a derecha
10O de bitsde izquierda a derecha
11&&de izquierda a derecha
12O lógicode izquierda a derecha
13?:de derecha a izquierda
14= += -= *= /= %= y compañíade derecha a izquierda
15,de izquierda a derecha

Las consecuencias prácticas:

a + b * c       /* significa a + (b * c) */
a < b == c < d  /* significa (a < b) == (c < d), rara vez lo que querias */
x & 1 == 0      /* significa x & (1 == 0), o sea x & 0: un error clasico */
*p++            /* significa *(p++): desreferencia p y luego avanzalo */

Esa tercera línea merece memorizarse: == se asocia más fuerte que &, así que las pruebas con máscaras de bits necesitan paréntesis: (x & 1) == 0.

Nadie recuerda los quince niveles, y nadie lo necesita. Aprende que * le gana a +, que las comparaciones le ganan a && y este a ||, y que todo lo demás lleva paréntesis:

if ((flags & MASK) != 0 && (count > 0 || force)) { ... }

Esa línea tiene paréntesis redundantes y es mejor gracias a ellos.

Preguntas frecuentes

¿Qué hace % en C?

% es el operador módulo: da el resto de la división entera. 17 % 5 es 2. Funciona solo con enteros; usarlo sobre un float o un double es un error de compilación, y fmod() de math.h es el equivalente en punto flotante.

¿Por qué 5 / 2 da 2 en C?

Porque ambos operandos son enteros, así que C hace división entera y descarta la fracción. Haz que un lado sea un valor de punto flotante para obtener 2.5: 5 / 2.0, o convierte un operando con (double)a / b.

¿Cuál es la diferencia entre i++ y ++i?

Ambos suman uno a i. ++i (prefijo) incrementa primero y produce el valor nuevo; i++ (sufijo) produce el valor viejo e incrementa después. Como sentencia suelta son idénticos: la diferencia solo importa cuando se usa el resultado, como en int b = a++; frente a int b = ++a;.

¿Qué es la precedencia de operadores en C?

El orden en que se asocian los operadores cuando una expresión no tiene paréntesis. *, / y % se asocian más fuerte que + y -, que lo hacen más que las comparaciones, que lo hacen más que &&, luego || y luego la asignación. Ante la duda, agrega paréntesis: no cuestan nada y eliminan la pregunta.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR