- Valores por la izquierda o lvalue (left value). Al evaluarlas devuelven la dirección de un cierto dato, que nos permitir acceder al mismo para inicializarlo, modificarlo, etc. Se llaman así porque son las expresiones que normalmente se colocan en el lado izquierdo del = en una expresión de asignación. Suelen ser nombres de variables, elementos de un arreglo, etc.
- Valores por la derecha o rvalue (right value). Al evaluarlas obtenemos un dato de cierto tipo. Normalmente son las expresiones que se colocan a la derecha del = en la asignación. Ejemplos de ellos son los contenidos de las variables, los datos contenidos en los arreglos y los valores constantes.
- Llamada a una función. Si la función no es de tipo void, al llamarla devolver un dato. Este dato puede ser usado como una expresión del tipo valor por la derecha.
Consideraremos a continuación dos tipos fundamentales de expresiones:
- la asignación
- las que utilizan operadores
El primer tipo a considerar dada su importancia es la asignación. La sintaxis de una asignación es:
lvalue = rvalue;
Para evaluar una asignación el compilador evalúa el lado derecho. El dato que obtiene es entonces cargado en la dirección que resulta de evaluar el lado izquierdo. Por ejemplo:
i = 1;
introduce el entero 1 en la dirección de la variable i. Como toda expresión, la asignación devuelve un valor, que es el mismo que se carga en la variable. Esto puede ser usado para inicializar varias variables con el mismo valor. Por ejemplo, si tenemos tres variables enteras i, j y k, la expresión:
i = j = k = 0;
las inicializa a 0 a las tres. Obsérvese que una misma variable puede estar en ambos lados de una asignación:
i = i + 1;
Esta expresión incrementa la variable i en una unidad.
Operadores
El segundo tipo de expresiones son los operadores. Hay varios tipos de operadores:
- aritméticos
- de incremento y decremento
- relacionales y lógicos
- de manipulación de bits
Los cuales realizan las operaciones aritméticas básicas. Estas expresiones tienen como sintaxis:
rvalue operador rvalue
Los operadores aritméticos son:
+ | operador de suma |
- | operador de diferencia |
* | multiplicación |
/ | división entera |
% | resto de la división (operador módulo) |
Por ejemplo:
i = a + 1;j = b * 4 + 10 / 2;
Operadores de incremento y decremento
Sirven para incrementar el valor de una variable. Admiten cuatro posibles combinaciones:
|
++lvalue | incrementa el contenido de lvalue y devuelve el nuevo contenido |
--lvalue | decrementa el contenido de lvalue y devuelve el nuevo contenido |
lvalue++ | incrementa el contenido de lvalue y devuelve el valor que contenía antes de incrementarlo |
lvalue-- | decrementa el contenido de lvalue y devuelve el valor que contenía antes de decrementarlo |
Por ejemplo:
i = j++; /* carga en i el valor de j y luego incrementa j*/i = ++j; /* incrementa j y luego carga su valor en i */
Estos operadores son muy usados en las variables de los bucles y con los punteros. El tipo de datos que devuelven es el del lvalue.
Operadores de comparación (relacionales) y lógicos
Los relacionales son:
< | menor que |
> | mayor que |
<= | menor o igual que |
>= | mayor o igual que |
== | igual que |
!= | distinto de |
Los lógicos son:
&& | Y |
|| | O |
! | NO |
La sintaxis para estas expresiones es:
rvalue operador rvalue
El valor que devuelve es de tipo entero: devuelve un 0 si el resultado de la comparación es falso (FALSE) y un valor distinto de 0 si el resultado de la comparación es verdadero (TRUE). En C el cero se toma como valor falso, y cualquier valor diferente del cero es verdadero. El valor concreto empleado para representar el valor verdadero es irrelevante, y normalmente depende del sistema empleado. Cuando lo que comparamos son caracteres, se compara realmente su código ASCII.
Por ejemplo:
1 > 2 devuelve un valor falso
1 == 1 devuelve un valor verdadero
'1' == 1 devuelve falso
'a' < 'b' es verdadero
1 || 0 devuelve verdadero
!1 devuelve falso
Operadores de manipulación de bits
Estos operadores muestran una de las armas más potentes del lenguaje C , la de poder manipular internamente, es decir bit a bit , las variables. Estos operadores sólo se aplican a variables del tipo char, short, int y long y no pueden ser usados con float o double.
Sabemos que las computadoras guardan los datos organizados en forma digital, en bytes, formados por números binarios de 8 bits y como se vio anteriormente, un char ocupa un byte de 8 bits , mientras que los short e int se forman con dos bytes (16 bits) y los long por cuatro bytes (32 bits).
Para el manejo de dichos bits, contamos con los operadores descriptos en la siguiente tabla .
& | Y (bit a bit) |
| | O inclusiva |
^ | O exclusiva |
<< | Desplazamiento a la izqda. |
>> | Desplazamiento a la dcha. |
~ | Complemento a uno |
Describiremos mediante unos pocos ejemplos la operatoria de manejo de bits. Analicemos primero como funciona el operador Y, también llamado BITWISE AND. Las reglas para la operación son las dadas en la siguiente tabla.
bit a | & | bit b | = | bit c |
0 | & | 0 | = | 0 |
0 | & | 1 | = | 0 |
1 | & | 0 | = | 0 |
1 | & | 1 | = | 1 |
Reglas para la operación Y (bit a bit)
Si suponemos tener dos variables del tipo char, una de ella de valor 85 (hex. 55 ), otra de valor 71 (hex. 47) y realizamos el Y a nivel bits de ellas, obtendremos :
bits decimal hexadecimal 0 1 0 1 0 1 0 1 85 55 & & & 0 1 0 0 0 1 1 1 71 47 ------------------------- ------- ------- 0 1 0 0 0 1 0 1 69 45
Nótese que la operación es del tipo lógico entre bits, por lo que los resultados numéricos tienen poco ó ningún significado y sólo se han puesto con fines de ejemplo.
De la misma manera para la operación O inclusiva, cuyas reglas se dan en la siguiente tabla:
bit a | | | bit b | = | bit c |
0 | | | 0 | = | 0 |
0 | | | 1 | = | 1 |
1 | | | 0 | = | 1 |
1 | | | 1 | = | 1 |
Reglas para la operación O inclusiva (bit a bit)
Para las mismas variables anteriores obtendremos el resultado:
0 1 0 1 0 1 1 1 87 57
Analizando ahora la O exclusiva tendremos :
bit a | ^ | bit b | = | bit c |
0 | ^ | 0 | = | 0 |
0 | ^ | 1 | = | 1 |
1 | ^ | 0 | = | 1 |
1 | ^ | 1 | = | 0 |
Reglas para la operación O exclusiva (bit a bit)
Para las mismas variables anteriores obtendremos:
0 0 0 1 0 0 1 0 18 12
Veamos ahora las operaciones de desplazamiento. La sentencia
c = a << b
implica asignarle a c el valor de a con sus bits desplazados a la izquierda en b lugares. Los bits que van "saliendo" por la izquierda , se desechan, y los bits que van quedando libres a la derecha se completan con cero .
Se procede de la misma manera para el desplazamiento a la derecha >>.
El operador complemento a uno (~) es del tipo unitario , es decir que realiza una operación sobre una única variable , y su efecto es dar a la variable un valor igual a restar de ( -1 ) el valor que traía . Quizás es más visible decir que este operador cambia los bits en 1 de la variable en 0 y viceversa.
Reglas de precedencia y asociatividad
En los operadores anteriores deben tenerse en cuenta la precedencia de los mismos y las reglas de asociatividad, que son las normales en la mayoría de lenguajes. Se debe consultar el manual de referencia para obtener una explicación detallada. Además, hay toda una serie de operadores aritméticos con asignación, como pueden ser += y ^=.
Precedencia | Operadores | Asociatividad |
0 | ()[] -> . | izqda. a dcha. |
1 | sizeof (tipo) ! ~ ++ -- signo* & | dcha. a izqda. |
2 | * / % | izqda. a dcha. |
3 | + - | izqda. a dcha. |
4 | > | izqda. a dcha. |
5 | >= | izqda. a dcha. |
6 | == != | izqda. a dcha. |
7 | & | izqda. a dcha. |
8 | ^ | izqda. a dcha. |
9 | | | izqda. a dcha. |
10 | && | izqda. a dcha. |
11 | || | izqda. a dcha. |
12 | ?: | dcha. a izqda. |
13 | = += -= *= etc | dcha. a izqda. |
NOTA: en la fila de los operadores de precedencia cero se han agregado a la derecha los tres operadores , [ ] ->> y .que serán analizados en otro lugar. De la misma manera, en el renglón siguiente hemos colocado al final los dos operadores * y &, ya que, aunque coinciden en símbolo con los de producto e Y (bit a bit), son otro tipo de operadores que se describirán en otro lugar. En ese mismo renglón se ha consignado como "signo" al unitario - .
NOTA: En la evaluación de expresiones lógicas, los compiladores normalmente utilizan técnicas de evaluación rápida. Para decidir si una expresión lógica es cierta o falsa, muchas veces no es necesario evaluarla completamente. Por ejemplo, en una expresión como <exp1> || <exp2>, el compilador evalúa primero <exp1> y si es cierta, no evalúa <exp2>. Por ello se deben evitar construcciones en las que se modifiquen valores de datos en la propia expresión, pues su comportamiento puede depender de la implementación del compilador o de la optimización utilizada en una compilación o en otra. Estos son errores que se pueden cometer fácilmente en C ya que una asignación es también una expresión.
Debemos evitar:
if (( x++ > 3 ) || ( x < y ))
y escribir en su lugar:
x++;
if (( x > 3 ) || ( x < y ))
Hay un tipo especial de expresión en C que se denomina expresión condicional y está representada por los operadores ? : . Su utilización es como sigue: <e> ? <x> : <y>. Se evalúa si e, si es cierto entonces x; si no, y.int mayor ( int a, int b ) { return ( a > b ) ? TRUE : FALSE; } inutil () { float a, b = 0.0; ( b > 0.0 ) ? sin(M_PI / 8) : cos(M_PI / 4); } |
0 comentarios:
Publicar un comentario