Operandos y Operadores
El lenguaje C++ tiene una cantidad apreciable de operadores. Podemos definir los operadores básicos:
- Aritméticos
- Relacionales
- Lógicos
- De bit
- Asignación
- De indirección
- Condicional
- sizeof
- De incremento/decremento
Los operadores actúan sobre operandos y los operandos pueden tener distintas formas: pueden ser variables, constantes, expresiones, etc. Antes de entrar en el tema de los operadores debemos definir el concepto del operando LValue. Cuando se indica que un operando es LValue se quiere expresar que es un operando que puede estar a la izquierda de una operación de asignación. Representa una dirección de memoria y es un objeto al que se le pueden asignar valores. Hay operandos que no cumplen estas condiciones (una constante o una expresión).
operadores aritméticos
Como es de suponer, C++ usa operadores para hacer los cálculos. Tiene cinco operadores aritméticos básicos: suma, resta, multiplicación, división y módulo. Una expresión es el conjunto de los operandos con su operador. Por ejemplo, las siguiente son expresiones:
int deuda = 100 – 40; // deuda es 60
int cuota = deuda / 10; // los operandos pueden ser variables
Orden de precedencia
i
nt cuota = deuda / 10 + 10; // ¿cuál es el resultado? 16 ó 3
Cuando en una operación aritmética hay más de un operador surge el problema de la precedencia. Por suerte, C++ sigue las mismas reglas que el álgebra. Primero se resuelven las operaciones de multiplicación, división y módulo y luego la suma y la resta. En el ejemplo anterior, el resultado correcto es 16. Tal como se utiliza en el álgebra, los paréntesis permiten modificar las prioridades.
int cuota = deuda / (10 + 10); // ¿cuál es el resultado? Ahora es 3.
Asociatividad
El orden de precedencia no resuelve todos los problemas. Si no se utilizan paréntesis pueden presentarse dudas sobre cómo actúa C++ ante ciertas expresiones. Por ejemplo,
int cuota = 600 * 10 / 10; // ¿cuál es el resultado? 60 o 600
La multiplicación y la división comparten el mismo orden de precedencia. En estos casos C++ utiliza la regla de asociatividad (izquierda a derecha o derecha a izquierda). La asociatividad izquierda a derecha indica que si dos operadores que actúan sobre un mismo operando tienen el mismo orden de precedencia, se aplica primero el operador de la izquierda. En el ejemplo anterior, el resultado correcto es 600, ya que la multiplicación y la división tienen asociatividad izquierda a derecha.
Más adelante se resumen los operadores y se indica, entre otras cosas, el tipo asociatividad.
Particularidad de la división
Al realizar una división se puede presentar la circunstancia que los dos operandos sean enteros, ental caso el resultado será entero y se perderá la parte fraccionaria. Si alguno (o los dos) de los operandos es de coma flotante, el resultado mantendrá esa precisión.
El operador módulo
El operador módulo nos permite obtener el resto de una división entre enteros.
int flanes = 5;
int hijos = 3;
int paraelpadre = flanes % hijos; // el padre come 2 flanes
Conversiones
Las operaciones aritméticas se pueden realizar con más de una docena de tipos diferentes. C++ simplifica gran parte del problema realizando una serie de conversiones de manera automática.
conversiones en asignaciones
Como regla general, cuando una variable se asigna a otra de distinto tipo, C++ se encarga de convertir la variable al tipo de la variable receptora antes de hacer la asignación.
Si el tipo de la variable receptora tiene mayor capacidad que el tipo de la variable emisora, no supone ningún problema. Por ejemplo, asignar una variable de tipo short a una variable de tipo long simplemente hará que la variable ocupe 2 bytes más.
El problema surge cuando el tipo de la variable receptora no permite almacenar el valor por excederse en el rango (en cuyo caso, el comportamiento de C++ varía entre las distintas implementaciones) o cuando el tipo de la variable receptora puede almacenar el valor pero perdiendo precisión en el resultado (por ejemplo, por perder la parte fraccionaria).
conversiones en expresiones
Cuando en una expresión se combinan dos tipos aritméticos diferentes C++ puede realizar conversiones automáticas. Hay dos tipos de conversiones automáticas. Primero, C++ realiza una conversión de promoción a int para eliminar una serie de tipos. Estos tipos que promueven a tipo int son los siguientes: short, char y bool (True es promovido a 1 y False es promovido a 0). Segundo, C++ realiza otra conversión automática cuando se encuentra con dos operandos de distinto tipo. Esta conversión generalmente supone que el tipo más pequeño se convierte al más grande.
El compilador realiza la conversión aplicando la siguiente lista en el orden en que se detalla. Es decir, cuando se encuentra el caso para el par de tipos aritméticos de la operación se realiza la conversión y ya no se sigue comprobando el resto de la lista.
Si un operando es | y el segundo es | se convierte a |
long double | resto (*) | long double |
double | resto | double |
float | resto | float |
unsigned long | resto | unsigned long |
long | unsignet int | unsigned int olong (**)long |
long | resto | long |
unsigned int | resto | insigned int |
int | int | no hay conversión |
(*) Resto significa cualquier otro tipo.
(**) Depende del tamaño relativo de los tipos, en lo psible se convierte a long.
Conversión explícita (CAST)
C++ nos permite realizar conversiones de manera explícita. De esa manera, podemos utilizar el tipo deseado para una operación. Por ejemplo, si para una operación nos conviene que una variable int sea considerada como char tendremos que codificar lo siguiente:
int k1;
char d;
….
d = char (k1);
También se puede codificar, con el mismo resultado, de la siguiente manera:
d = (char) k1; // sintaxis propia de C pero válida en C++
Pero se prefiere la primera forma que se parece a la llamada de una función.
Operadores relacionales
C++ provee seis operadores relacionales para poder comparar números. El resultado de la comparación se reduce a dos posibilidades: True y False. Los operadores relacionales son los siguientes:
- > mayor que
- < menor que
- >= mayor o igual
- <= menor o igual
- == igual
- != no igual
Los operadores tienen una precedencia menor que los operadores aritméticos. Por ejemplo, si se tiene la siguiente expresión:
a + 3 > 5 * k
Esta expresión podría leerse de la siguiente manera:
(a + 3) > (5 * k) // correcta
Pero si se convirtiese de esta manera, el resultado sería incorrecto:
a + ( 3 > 5) * k // incorrecta
Operadores lógicos
El operador lógico AND (&&) realiza el producto lógico entre dos expresiones. El operador lógico AND tiene menor precedencia que los operadores relacionales. Si la expresión de la izquierda es False, el resultado será False sin necesidad de evaluar la expresión de lado derecho. Estos son algunos ejemplos del operador &&:
-3 && 3 // expresión True, valor 1
6 < 2 && 8 > 7 // expresión False, valor 0
7 > 1 && 7 == 7 // expresión True, valor 1
El operador OR || indica una suma lógica de dos expresiones. El operador lógico OR tiene menor precedencia que los operadores relacionales y que el operador lógico AND. Estos son algunos ejemplos del operador OR:
4 == 4 || 2 > 4 // expresión True
0 || 0 // expresión False
El operador OR actúa como un separador de expresiones, es decir, se evalúa la expresión que queda a la izquierda del operador y luego la expresión de la derecha.
El operador lógico NOT (!) invierte el valor de la expresión que tiene a continuación. Este operador tiene una precedencia mayor que todas las operaciones aritméticas (y obviamente que las relacionales). Estos son algunos ejemplos del uso del operador NOT:
!(k > m) // si k es mayor a m, el resultado será False
!k > 1 // k puede ser True o False (0 o 1)
// según sea el valor de k 0 o no cero
// el resultado siempre será False
Operadores de bit
Los operadores de bit sólo operan con valores enteros. Los operadores de bit son los siguientes:
- & producto binario de bits
- | suma binaria de bits (OR)
- ^ suma exclusiva de bits (XOR)
- << desplazamiento de bits hacia la izquierda
- >> desplazamiento de bits hacia la derecha
- ~ unario. complemento a 1
- – unario. complemento a 2
operador AND de bit (&)
El operador & multiplica bit a bit dos números enteros y da como resultado un nuevo valor entero. Cada bit resultante será cero si alguno de los dos bits de la multiplicación son cero.
Los operadores a nivel de bit son análogos los operadores lógicos normales, pero actúan bit a bit. Por ejemplo,
2 & 1 // False, es cero
2 && 1 // true, valor 1
operador OR de bit (|)
El operador | suma bit a bit dos números enteros y da como resultado un nuevo valor entero. Cada bit resultante será uno si alguno (o ambos) de los dos bits de la suma es uno.
operador XOR de bit (^)
El operador ^ suma exclusiva bit a bit dos números enteros y da como resultado un nuevo valor entero. Cada bit resultante será uno sólo si uno de los dos bits de la suma es uno.
operador desplazamiento de bits a la izquierda (<<)
El operador << actúa sobre valores enteros y tiene la siguiente sintaxis:
valor1 << desp1
En donde desp1 es un valor entero que indica la cantidad de bits que hay que desplazar hacia la izquierda en valor1. Se produce un descarte de bits por la izquierda y una entrada de bits (en cero) por la derecha.
operador desplazamiento de bits a la derecha (>>)
El operador >> actúa sobre valores enteros y tiene la siguiente sintaxis:
valor1 >> desp1
En donde desp1 es un valor entero que indica la cantidad de bits que hay que desplazar hacia la derecha en valor1. Se produce un descarte de bits por la derecha y una entrada de bits (en cero) por la izquierda.
Operador unario negación de bit (~)
El operador ~ actúa sobre los bits realizando el complemento 1, o sea, cambia unos por ceros.
Hay una analogía en el funcionamiento del operador de negación NOT (si la expresión evalúa True, el resultado será False) con el operador de negación de bit (los bits que están en cero pasan a 1 y viceversa).
Operador unario (-)
El operador – actúa sobre los bits realizando el complemento 2, o sea, cambia el signo.
Operadores de asignación
El operador de asignación hace que el operando que está a la izquierda del signo igual tome el valor de la expresión que está a la derecha del signo igual.
a = b + 2;
a = a + 2;
a+= 2; // es igual al anterior. El operador de asignación
a*=2; // también se pueden combinar con otros operadores
a/=2;
Operadores de indirección
El apuntador contiene la dirección de un valor y el nombre de un apuntador representa esa dirección. El operador *, denominado operador de indirección (dereferencing operator), se refiere al valor de que hay en esa dirección. Por ejemplo, si ptr1 es un apuntador, entonces ptr1 representa una dirección y *ptr1 representa el valor que hay almacenado en esa dirección.
El símbolo * es el mismo que se utiliza para la multiplicación, C++ utiliza el contexto para determinar si lo que estamos haciendo es una multiplicación o una indirección.
Operador condicional
El operador condicional ? se suele utilizar como alternativa a la sentencia if else. Es el único operador que requiere tres operandos. La sintaxis es la siguiente:
expresión1 ? expresión2 : expresión3
Por ejemplo,
cmdOK = (ingresos > egresos) ? True : False;
Si la expresión1 es verdadera se asigna (en este caso) True a cmdOK, en caso contrario, se asigna False.
Se podría haber expresado de la siguiente manera:
if ingresos > egresos
cmdOK = True;
else
cmdOK = False;
La sintaxis con el operador ? es más concisa pero a veces no tan legible como la conocida sentencia if else.
Operador sizeof
El operador sizeof nos permite conocer el tamaño en bytes de un tipo o de una variable. Supóngase que se tienen estas definiciones:
int k;
long j:
char b;
sizeof (k) no da el valor 2
sizeof (j) nos da el valor 4
sizeof (b) nos da el valor 1
sizeof (float) nos da el valor 4 (tipo float ocupa 4 bytes)
Si se aplica sizeof a una referencia, nos dará como resultado la cantidad de bytes ocupados por el objeto que apunta la referencia.
Operador de incremento/decremento
Éste es un operador muy peculiar de C++. El operador ++ suma uno a la variable, la que debe ser de tipo LValue (variable que puede estar a la izquierda de una expresión, o sea, que puede recibir una asignación de valor).
i++; // suma uno a i
i = i +1; // expresión equivalente
k = 1;
k++; // k es igual a 2
j = k++; // j es igual a 2 pero k vale 3 ¿Porqué?
Existen dos tipos de incrementos:
- Preincremento: suma y asigna (++ está a la izquierda de la variable)
- Postincremento: asigna y suma (++ está a la derecha de la variable)
j = ++k; // j es igual a 4 y k es igual a 4
j = k++; // j es igual a 4 y k es igual a 5
Todo lo dicho para el operador incremento es transladable para el operador decremento (—), la diferencia es que este operador resta en lugar de sumar.
j = (++k)—; // j es igual a 6, k sigue siendo 5