Operadores y tipos de datos en C

Anuncio
Operadores y tipos de datos en C
Vamos a hacer un repaso de algunos de los operadores y de los tipos de datos numéricos
que podemos encontrar en C.
Tipos de datos numéricos
Antes de nada veamos con que tipos de datos numéricos podemos trabajar en C.
C nos permite definir nuestros propios tipos de datos a partir de los tipos ya predefinidos.
Esto lo veremos más adelante.
Consideraciones previas a la declaración de variables
Antes de ver los tipos de datos recordaremos un par de cosas sobre variables y tipos de
datos:
1. En C es necesaria la declaración de variables, no se puede utilizar una variable que
no haya sido previamente declarada.
2. Cuando creamos una variable, hay que inicializarla. Cuando C crea una variable no
la inicializa a cero ni a ningún otro valor y antes de utilizarla deberemos inicializarla
(asignarle un valor) ya que en caso contrario estará inicializada al valor que hubiera
en esa zona de memoria, normalmente conocido como basura, y al utilizar la
variable podremos encontrarnos con funcionamientos anómalos.
3. Las variables se han de declaran al inicio de los bloques de código y podrán ser
accedidas dentro de ese bloque de código. Un ejemplo:
#include <stdio.h>
int main (void)
{
int pepito = 0,
Pepito = 0,
i;
float num = .0;
....
for(i=0;i<=pepito;i++) {
double saldo = 0.; /* Variable valida unicamente dentro del
bucle FOR */
....
}
return 0;
}
4. C es Case Sensitive, es decir distingue entre mayúsculas y minúsculas. Para C las
variables pepito y Pepito son variables distintas.
5. Los nombres de las variables son secuencias de letras y números que no pueden
contener caracteres especiales ni espacios en blanco y tienen que empezar
forzosamente por una letra o el carácter _.
Tipos de datos enteros
En estos tipos de datos se almacenarán números enteros.
Para declarar una variable de este tipo se utiliza la palabra reservada int:
int pepito = 0;
Con esto hemos declarado una variable llamada pepito y la hemos inicializado a cero.
Podemos crear otros tipos de datos enteros:





short pepito, declara a pepito como una variable de tipo short. Este tipo de
variable puede contener datos de tipo entero pero de un rango más pequeño que int.
Ocupa menos memoria que un dato del tipo int.
unsigned int pepito, declara a pepito como una variable de tipo entero sin signo,
por lo tanto podrá almacenar unicamente enteros positivos. Ocupa la misma
memoria que un dato del tipo int.
long int pepito, declara a pepito como una variable de tipo entero largo, en ella se
podrán almacenar números enteros mayores que en un dato del tipo int. Ocupa más
memoria que un dato del tipo int.
unsigned short int pepito, declara a pepito como una variable de tipo short sin
signo. Ocupa la misma memoria que un dato del tipo short int.
unsigned long int pepito, declara a pepito como una variable de tipo entero largo
sin signo, en ella se podrán almacenar números enteros positivos. Ocupa más
memoria que un dato del tipo int y la misma cantidad que un dato long int.
Mediante el uso del operador sizeof podemos ver la cantidad de espacio que utiliza
cualquier tipo de dato, en particular los tipos de datos enteros:
#include <stdio.h>
int main (void)
{
int entero = 0;
short entero_corto = 0;
unsigned int entero_positivo = 0;
long int entero_largo;
unsigned long int entero_largo_positivo = 0;
unsigned short int entero_corto_positivo = 0;
printf("int: %d Bytes\n", sizeof(entero));
printf("short: %d Bytes\n", sizeof(entero_corto));
printf("unsigned int: %d Bytes\n", sizeof(entero_positivo));
printf("unsigned short int: %d Bytes\n",
sizeof(entero_corto_positivo));
printf("long int: %d Bytes\n", sizeof(entero_largo));
printf("unsigned long int: %d Bytes\n", sizeof(entero_largo_positivo));
return 0;
}
Dentro del fichero de cabecera limits.h podemos ver los rangos de números que nuestro
compilador soporta. Los aquí listados se corresponden con los de mi máquina. En otros
sistemas y/o arquitecturas pueden variar.
También exiten constantes que nos dicen el valor máximo y mínimo de cada tipo de dato.
Para acceder a ellas necesitamos incluir en nuestro programa en C la directiva #include
<limits.h> y ya tendremos acceso a dichas variables.
Tabla 1.
Tipo de dato Bytes Valor
Mínimo
Valor
Máximo
Cte Valor
Mínimo
Cte Valor
Máximo
int
4
-2147483648 2147483647
INT_MIN
INT_MAX
short
2
-32768
32767
SHRT_MIN
SHRT_MAX
unsigned int
4
0
4294967295
-
UINT_MAX
unsigned
short
2
0
65535
-
USHRT_MAX
long int
4
-2147483648 2147483647
LONG_MIN
LONG_MAX
0
-
ULONG_MAX
unsigned long 4
int
4294967295
Tipos de datos de punto flotante
En estos tipos de datos se almacenarán números en punto flotante.
En C tenemos basicamente dos tipos de datos en punto flotante:

float, punto flotante simple precisión.

double, punto flotante doble precisión.
Las características de este tipo de datos estan incluidas en el fichero float.h. En mi
máquina tenemos:
Tabla 2.
Tipo
de
dato
Bytes Valor Mínimo
Valor Máximo
Cte Valor
Mínimo
Cte Valor
Máximo
float
4
3.40282347e+38
FLT_MIN
FLT_MAX
double 8
2.2250738585072014e- 1.7976931348623157e+308 DBL_MIN
308
DBL_MAX
long
12
double
3.362103e-4932
1.17549435e-38
1.189731e+4932
LDBL_MIN LDBL_MAX
Otras constantes interesantes son:






FLT_EPSILON, distancia entre dos números consecutivos representables en datos
del tipo float.
DBL_EPSILON, distancia entre dos números consecutivos representables en datos
del tipo double.
LDBL_EPSILON, distancia entre dos números consecutivos representables en
datos del tipo long double.
FLT_DIG, número de digitos decimales de un dato del tipo float.
DBL_DIG, número de digitos decimales de un dato del tipo double.
LDBL_DIG, número de digitos decimales de un dato del tipo long double.
Constantes
Para definir constantes en C lo haremos en la zona de directivas al preprocesador, es decir
al principio del fichero y lo haremos con la directiva #define:
#include <stdio.h>
#define PI 3.141592654
...
En este ejemplo hemos definido una constante llamada PI y con valor 3.141592654. Una
vez declaramos una constante en C no es posible alterar ni modificar su valor.
Conversión entre diferentes tipos de datos
¿Qué pasaría si intentaramos almacenar una variable de tipo float en una de tipo int?
...
int a = 0;
float b = 4.67896;
...
a = b;
...
Pues el compilador intentará convertirla de la mejor forma posible, normalmente truncando
el valor. Puede ser que en nuestro compilador estos "ajustes" se realicen bien la mayoría de
las veces o incluso siempre. Pero algún día podemos utilizar otro compilador diferente, ya
sea por que se ha instalado una versión nueva o estemos en otra máquina y ese compilador
no funcione tan bien.
Con el fin de evitarnos futuros dolores de cabeza es conveniente realizar una operació de
cast.
Para hacer un cast introduciremos entre parentesis el tipo de dato de destino y lo
antepondremos al dato que queremos convertir, por ejemplo en el ejemplo anterior:
...
int a = 0;
float b = 4.67896;
...
a = (int)b; /* Esto es un cast de un tipo float, b, a un tipo int, a. */
...
Esta es la forma correcta de hacerlo. Este tipo de conversión es ampliamente utilizada en el
uso de punteros, para forzar la conversión de forma correcta a nuestro tipo de puntero y
evitar de esta forma "deslices" con las posiciones de memoria. Esto ya lo veremos más
adelante.
El operador de asignación =
El operador asignación es = y su funcionamiento es el mismo que en el resto de lenguajes
de programación.
Operadores aritméticos estándar
En C podemos encontrar los siguientes operadores aritméticos:
1.
2.
3.
4.
5.
+, este operador realiza la suma de dos operandos.
-, este operador realiza la resta de dos operandos.
*, este operador realiza la multiplicación de dos operandos.
/, este operador realiza la división de dos operandos.
%, este operador recibe el nombre de operador de módulo y devuelve el resto de
dividir el primer operador entre el segundo.
El operador +
Este operador se aplica a los datos numéricos y su compartamiento es similar al que tienen
otros lenguajes.
El operador Este operador se aplica a los datos numéricos y su compartamiento es similar al que tienen
otros lenguajes.
El operador *
Este operador se aplica a los datos numéricos y su compartamiento es similar al que tienen
otros lenguajes.
El operador /
Este operador se aplica a los datos numéricos y su compartamiento puede diferir al de otros
lenguajes.
Basicamente su comportamiento en como en el resto de lenguajes, pero hay que tener en
cuenta una característica que no este presente en algunos lenguajes.
Si los operandos utilizados con este operador son enteros se realiza la división ENTERA y
no la decimal:
a
b
c
d
=
=
=
=
7/3;
5.0/3.0;
6.0/4;
7/2.0;
Los resultados obtenidos serán:

a = 7/3;, en este caso el valor de la variable a sería 2 ya que ambos operandos son
enteros y se realiza la división entera.



b = 7.0/3.0;, en este caso el valor de la variable b sería 2.33333 ya que al ser los
operandos números decimales se realiza la división decimal.
c = 6.0/4;, en este caso el valor de la variable c sería 1.5 ya que al ser uno de los
operandos un número decimal se realiza la división decimal.
d = 7/2.0;, en este caso el valor de la variable d sería 3.5 ya que al ser uno de los
operandos un número decimal se realiza la división decimal.
El operador %
Este operador se aplica a datos enteros y devuelve el resto de dividir el primer operando
entre el segundo:
a = 5 % 3;
b = 5 / 3;
c = 3 * b + a;



a = 5 % 3;, en este caso el valor de la variable a sería 2 que es el resto de dividir, de
forma entera, 5 entre 3.
b = 5 / 3;, en este caso el valor de la variable b sería 1 que es el resultado de la
división entera de 5 entre 3.
c = 3 * b + a;, en este caso el valor de la variable c sería 5, Algoritmo de Euclides
más conocido como:
Dividendo es igual a divisor por cociente más el resto.
Operadores compuestos de asignación
C dispone de unos operadores especiales de asignación que no estan presentes en otros
lenguajes. Los lenguajes derivados de C suelen tenerlos con lo cual si ya has programado
alguna vez con algunos de estos lenguajes ya estarás acostumbrado a ellos.
El operador +=
Este operador incrementa una variable en la cantidad especificada:
...
b += 6.5; /* Equivale a: b = b + 6.5; */
...
El operador -=
Este operador decrementa una variable en la cantidad especificada:
...
c -= 89; /* Equivale a: c = c - 89; */
...
El operador *=
Este operador multiplica una variable por la cantidad especificada:
...
d *= 3.14159; /* Equivale a: d = d * 3.14159; */
...
El operador /=
Este operador divide una variable por la cantidad especificada:
...
e /= 2.5; /* Equivale a: e = e / 2.5; */
...
Al igual que el operador / si ambos operandos son números enteros realizará la división
entera.
El operador %=
Este operador asignará a la variable su resto al dividir por el segundo operando:
...
f %= 6; /* Equivale a: f = f % 6; */
...
Operadores aritméticos unarios
Estamos acostumbrados a que los operadores actuen sobre dos operandos, pero en C
tenemos también operadores que actuan sobre un único operando.
Entre estos operadores podemos destacar los operadores pre/post incremento y pre/post
decremento.
El operador pre/post incremento ++
Este operador incrementa una variable en una unidad.
Este operador se puede anteponer o postponer a la variable y/o expresión sobre la que se
quiera actuar. El comportamiento del operador varía según se anteponga o postponga:
++a;
b++;
(c+d)++;
++(e*f);




++a;, en este caso se incrementa la variable en una unidad para ser utilizada después
de ser incrementada.
b++;, en este caso se incrementa la variable después de ser utilizada.
(c+d)++;, en este caso se evalua la expresión c+d, se utiliza y después se
incrementa.
++(e*f);, en este caso se evalua la expresión e*f, se incrementa y después se utiliza.
Para el programador que no este familiarizado con este tipo de operadores es posible que no
haya entendido muy bien el funcionamiento de este tipo de operador. Para aclararlo más
veamos un ejemplo:
/* autoincremento.c */
#include <stdio.h>
/* Ejemplo del operador ++ */
int main (void) {
int
i=0;
printf("Ejemplo de preincremento:\n\t");
while ( i <= 5)
printf("%d ", ++i);
printf("\nEjemplo de postincremento:\n\t");
i = 0;
while ( i <= 5)
printf("%d ", i++);
printf("\n");
return 0;
}
Si ejecutaramos ester programa:
jose@debian:~$ ./autoincremento
Ejemplo de preincremento:
1 2 3 4 5 6
Ejemplo de postincremento:
0 1 2 3 4 5
jose@debian:~$
El programa hace lo siguiente:
1. Se declara e inicializa una variable a cero.
2. Se imprime una cadena de texto.
3. Mientras la variable i sea menor o igual que 5 se ejecuta:
printf("%d ", ++i);
Es decir se incrementa la variable i en una unidad y se imprime su valor.
4. Se imprime una cadena de texto.
5. Se inicializa la variable i a cero.
6. Mientras la variable i sea menor o igual que 5 se ejecuta:
printf("%d ", i++);
Es decir se imprime el valor de la variable i y se incrementa su valor en uno.
El operador pre/post decremento -Este operador decrementa una variable en una unidad.
Este operador funciona exactamente igual que el operador pre/post incremento con la
excepción de que este decrementa el valor de las variables y/o expresiones en una unidad
en lugar de incrementarlo.
El operador unario Si anteponemos este operador a una expresión y/o variable alteraremos su signo.
Operadores relacionales
Estos operadores se utilizan para comprobar relaciones entre sus operandos.
Todos estos operadores devuelven 1 si la relación es cierta y 0 si es falsa.
El operador de desigualdad <
Este operador se utiliza para comprobar si un operando es menor que el otro.
El operador de desigualdad >
Este operador se utiliza para comprobrar si un operando es mayor que el otro.
El operador de desigualdad <=
Este operador se utiliza para comprobrar si un operando es menor o igual que el otro.
El operador de desigualdad >=
Este operador se utiliza para comprobrar si un operando es mayor o igual que el otro.
El operador de igualdad ==
Este operador se utiliza para comprobar si ambos operadores son iguales.
El operador de igualdad !=
Este operador se utiliza para comprobar si ambos operadores son distintos.
Operadores lógicos
Estos operadores se utilizan para crear expresiones más complejas utilizadas en expresiones
condicionales, habitualmente como condiciones para If.
En C el valor 1 se entiende como VERDADERO y 0 como FALSO.
El operador lógico &&
Este operador necesita dos operandos y también se le conoce como Y lógico:


Si ambos operandos son ciertos la expresión es valida y devuelve el valor 1.
Si alguno de los operandos es falso la expresión es falsa y devuelve el valor 0.
Por ejemplo:
...
if ( a!= 0 && b < 6 ) {
...
}
else {
...
}
...
El cón incluido en el bloque if se ejecutará siempre que a sea distinto de cero y b sea menor
que seis. En el resto de casos se ejecutarán el bloque else.
El operador lógico ||
Este operador necesita dos operandos y también se le conoce como O lógico:


Si ambos operandos son falsos la expresión es falsa y devuelve el valor 0.
Si alguno de los operandos es cierto la expresión es cierta y devuelve el valor 1.
Por ejemplo:
...
if ( a!= 0 || b < 6 ) {
...
}
else {
...
}
...
El cón incluido en el bloque if se ejecutará siempre que a sea distinto de cero o b sea menor
que seis. En el resto de casos se ejecutarán el bloque else. Es decir el bloque if se ejecutará
cuando al menos una de las dos expresiones sea cierta.
Operador lógico !
Este operador necesita un operando y también se le conoce como negación lógica:


Si el operando es falso devuelve verdadero.
En el resto de casos devuelve falso.
Por ejemplo:
...
if ( !a ) {
...
}
else {
...
}
...
El cón incluido en el bloque if se ejecutará siempre que a sea falso, cero, y en el resto de
casos se ejecutarán el bloque else.
Operadores sobre bits
C dispone de operadores de bajo nivel que actuan sobre bits. Desde el punto de vista de la
programación científica sólo nos interesan los operadores de desplazamiento de bits o
bitwise. Estos operadores actuan sobre datos de tipo entero.
Los operadores a nivel de bit de C:




El operador AND, &, que realiza el Y lógico bit a bit de sus dos operandos.
El operador OR inclusivo, |, que realiza el O lógico bit a bit de sus dos operandos.
El operador OR exclusivo, ^, que realiza el OR exclusivo bit a bit de sus dos
operandos.
El operador de negación de bits o de complemento a uno, ~, que actua sobre un
operando invirtiendo sus bits.
Estos operadores tienen sus correspondientes operadores compuestos de asignación que son
&=, |= y ^= respectivamente.
El operador de desplazamiento de bits a la izquierda <<
Este operador desplaza bits hacia la izquierda tantas veces como le indiquemos.
Sea a=57 su representación en binario será:
00000000000000000000000000111001
Si desplazamos 4 bits hacia la izquierda tendremos:
00000000000000000000001110010000
Que en decimal es 912.
Desplazar n bits hacía la izquierda equivale a multiplicar por 2^n. Es una forma más rápida
que calcular 2^n y luego hacer la multiplicación.
Un ejemplo:
/* bitwise.c */
#include <stdio.h>
int main (void)
{
int a=57,
b = 0,
num = 0,
mul = 1,
i = 0;
printf("Numero de bits a desplazar: ");
scanf("%d", &num);
/* Calculamos 2^num */
for(i=0;i<num;i++)
mul *= 2;
printf("a = %d\n", a);
b = a << num;
printf("b = %d\n", b);
printf("a * 2^%d = %d\n", num, a*mul);
return 0;
}
Si ejecutaramos el programa:
jose@debian:~$ ./bitwise
Numero de bits a desplazar: 4
a = 57
b = 912
a * 2^4 = 912
jose@debian:~$
También existe el operador compuesto de asignación <<=:
...
a <<= b; /* Equivale a: a = a << b */
...
El operador de desplazamiento de bits a la derecha >>
Este operador desplaza bits hacia la derecha tantas veces como le indiquemos.
Funciona igual que el operador <<, sólo que desplaza hacía la derecha en lugar de hacía la
izquierda.
Desplazar n bits hacía la derecha equivale a realizar la divión entera por 2^n.
También existe el operador compuesto de asignación >>=:
...
a >>= b; /* Equivale a: a = a >> b */
...
Fuente: http://www.augcyl.org/glol/progcien2.html
Descargar