Técnicas de Programación 3.1.- Lenguaje de programación C Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores Introducción En lenguaje C podemos trabajar con distintos tipos de datos, como texto, números enteros, números reales enteros o fraccionarios, y otros muchos. Además, estos tipos de datos pueden tener distinto rango y/o precisión, podrán ser sólo positivos o tener signo, etc. En definitiva en C existen lo que se denominan tipos fundamentales y otros que son consecuencia de estos que se denominan datos derivados. Lo que vamos a comenzar por explicar es cuales son estos datos y como se almacenan en lenguaje C. Lo primero que debemos saber es que en C es necesario declarar todas las variables que se vayan a utilizar. Una variable no declarada produce un mensaje de error de compilación. Cuando declaramos una variable se reserva una determinada cantidad de memoria que depende del tipo de dato al que se destina. Es posible inicializar (dar un valor inicial) las variables cuando las declaramos, aunque si nos las inicializamos es, en ciertas ocasiones, el compilador el que les da un valor inicial por defecto, aunque en otros casos no se inicializan y la memoria asociada con la variable contendrá lo que se conoce como “basura” o cadenas de caracteres sin sentido. Los datos de tipo entero pueden calificarse con las palabras signed o unsigned. Un entero precedido de signed es un número entero con signo; o sea, un entero positivo o negativo. Un entero calificado con unsigned es un valor entero sin signo; es decir, valor entero positivo. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 1 Tipos de datos char El tipo char o carácter se utiliza para declarar datos enteros con un tamaño de byte, es decir 8 bits. Si se trata de un tipo signed, un bit se empleara para especificar el signo resultando un rango entre -128 y 127. Si se trata de tipo unsigned el rango será 0 a 255, siendo los primeros 128 (0 a 127) caracteres los códigos internacionales ASCII, ANSI o UNICODE empleados para la representación de caracteres. Tipo char (Abreviatura de signed char) unsigned char Tamaño Rango 8 bits 8 bits -128 a 127 0 a 255 La sintaxis para declarar variables de tipo char es: [signed|unsigned] char <nombre_variable> Si no especificamos signed o unsigned se asume signed char. Veamos algunos ejemplos: char caracter = ‘a’; /* A la variable caracter le hemos asignado como valor inicial el caracter o letra a minúscula. Obsérvese que ‘a’ y a no significan lo mismo, a entrecomillada se interpreta como un valor entero (un caracter) y a sin comillas es una variable. */ char caracter = 97; // La ‘a’ es el número decimal 97 char caracter = 0x61; // La ‘a’ es el número hexadecimal 0061 char caracter = 0141; // La ‘a’ es el número octal 0141 char variable; // Variable sin inicializar char variable1, variable2; // Declaración de más de una variable (del mismo tipo) en una sola sentencia La sentencia char caracter = ‘a’; indica que a partir de declarar la variable esta toma el valor correspondiente a la letra a. En realidad se guarda como un entero de un byte que no es otro que el correspondiente a la letra a en el código ASCII estándar que podemos ver en la página siguiente. También existe un código ASCII extendido que utiliza los 256 valores y que contiene caracteres especiales y caracteres específicos de los alfabetos de diversos países, como por ejemplo las vocales acentuadas y la letra ñ para el castellano. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 2 Tipos de datos 0 1 2 3 4 5 6 7 8 9 0 nul soh stx etx eot enq ack bel bs ht 1 nl vt np cr so si dle dc1 dc2 dc3 2 dc4 nak syn etb can em sub esc fs gs 3 rs us sp ! “ # $ % & ‘ 4 ( ) * + , - . / 0 1 5 2 3 4 5 6 7 8 9 : ; 6 < = > ? @ A B C D E 7 F G H I J K L M N O 8 P Q R S T U V W X Y 9 Z [ \ ] ^ _ ` a b c 10 d e f g h i j k l m 11 n o p q r s t u v w 12 x y z { | } ~ del Podemos observar que el código ASCII asocia números consecutivos para las letras, lo que facilita mucho operaciones de ordenación alfabética de palabras. En la tabla aparecen sombreados los caracteres no imprimibles, como por ejemplo el ht (tabulador horizontal), el nl (nueva línea), sp (espacio), etc. Veamos algunos ejemplos más. char caracter = 'a'; // Definimos e inicializamos la variable caracter. caracter = 'x'; //Variamos el contenido de la variable con otra sentencia. También podemos utilizarse una variable char para dar valor a otra variable de tipo char: letra = caracter ; // La variable letra toma el valor ’x’ También podremos realizar operaciones del tipo: letra = letra + 1; // si letra era una x, al incrementarse en uno pasa a valer y. En código ASCII la diferencia numérica entre letras minúsculas y mayúsculas es siempre de 32, lo que puede resultar bastante útil para cambiar a minúscula una letra mayúscula sumando esta diferencia. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 3 Tipos de datos short El tipo short se utiliza para definir datos con un tamaño de 16 bits (2 bytes) con independencia del ordenador utilizado. Tipo Tamaño Rango short (Abreviatura de signed short int) 16 bits -32,768 a 32,767 unsigned short 16 bits 0 a 65535 En el ejemplo siguiente se declaran variables de tipos short inicializadas a cero. short x = 0, y = 0; // declara variables tipo short con signo unsigned short a = 0, b = 0, c = 0; // declara variables tipo short sin signo int Originalmente una variable de tipo int ocupa 2 bytes, aunque lo normal es que utilice 4 bytes. En ANSI C esto depende del compilador. Tipo int (Abreviatura de signed int) unsigned int Tamaño Rango 32 bits 32 bits -2147483648 a 2147483647 0 a 4294967295 El tipo int se utiliza para definir datos con un tamaño de 32 bits (4 bytes). Este tipo de datos es útil para números relativamente grandes sin decimales. En el ejemplo se declaran e inician tres variables de este tipo. int x = 21453; // declara variable x de tipo int con signo int y = -274; // declara variable y de tipo int con signo int z = 0xF0FF; // declara variable z de tipo int con signo en valor hexadecimal Si por algún motivo a una variable int se le asigna en tiempo de ejecución un valor que esté fuera del rango permitido se produce un desbordamiento u overflow que origina un error en el resultado del que el programa no avisa, por lo que se trata de un error de programación difícil de detectar. Veremos esto al final de los tipos de datos. Si necesitamos ahorrar memoria podemos asegurar un tamaño de 2 bytes declarando las variables de la forma siguiente: short variable; short int variable; Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 4 Tipos de datos long El tipo long se utiliza para definir datos con un tamaño de 32 bits (4 bytes). En general podemos decir que la longitud de un int es menor o igual a la de un long Tipo Tamaño Rango long (Abreviatura de signed long) unsigned long 32 bits -2147483648 a 2147483647 32 bits 0 a 4294967295 En el ejemplo se declaran variables de tipo long. long x = 255; long y = 0x1F00350A; // valor en hexadecimal long a = -1L; // L indica que el valor -1 es de tipo long long int z; //sería igual que long z; ya que la palabra clave int puede omitirse float El tipo float se utiliza para declarar datos en formato de coma flotante según la norma IEEE 754, que emplea 24 bits para la mantisa (1 para el signo y 23 para el valor) y 8 bit para el exponente (1 para el signo y 7 para el valor). Se trata de números decimales aptos para variables de tipo real o para números muy grandes. No son números con una gran precisión radicando su importancia en el orden de magnitud del número, es decir, lo grande o lo pequeño que es el número. Tipo float Tamaño Rango 32 bits 2.93873 · 10-39 a 1,70141 · 10+38 Precisión 6 decimales Las variables tipo float se declaran e inicializan de la misma forma que las vistas: float NumReal; float x = 3.141592; float y = 2.2e-6; //2.2e-6 = 2.2 por 10 elevado a -6 float m = 2/3.0; Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 5 Tipos de datos float Ejemplo 6.- Precisión por tamaño en los tipo float Veamos un ejemplo que nos aclare el extremo citado de precisión por tamaño. Si realizamos las operaciones correspondientes a las siguientes instrucciones: float x = 12345678901234; x = x + 1; x = x - 12345678901234; El resultado esperado de las operaciones es 1, pero vemos que el resultado de la ejecución de un módulo de aplicación como el siguiente: //Precisión de los números de tipo float #include <stdio.h > int main() { float x = 12345678901234; printf("Imprecision por tamano en los tipo float"); x = x + 1; x = x - 12345678901234; printf("\nValor final obtenido: "); printf("%f", x); return 0; } Nos produce el siguiente resultado, que vemos que es cero y no uno, lo que indica que el tipo float sacrifica precisión por tamaño. Probamos que para valores más pequeños el resultado será correcto. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 6 Tipos de datos float Ya henos dicho que un número de tipo float se almacena en 4 bytes (32 bits) y que los reparte en 24 para la mantisa y 8 para el exponente. Vamos a ver que tipo de números de coma flotante se pueden representarse de esta forma. Para ello vamos a comenzar por distinguir el rango de la precisión, que es el número de cifras con las que se representa la mantisa, por lo que con 23 bits (uno es para el signo) el número más grande que se puede representar en base 2 es: 223 = 8 388 608 Vemos que el resultado tiene 7 cifras, lo que indica que se pueden representar todos los números de 6 o menos cifras y de 7 cifras hasta ese límite. Es decir, la precisión de un float está, dependiendo del número, entre 6 y 7 cifras no pudiendo representarse números mayores al resultado obtenido. Por otro lado, para el exponente tendremos que multiplicar la mantisa en base 2 (con 7 bits el número más grande que se puede representar es 27-1= 127), por lo que el rango superior será: 2127 = 1,70141 · 1038 El número más pequeño, en valor absoluto, será: 2-128 = 2.93873 · 10-39 Que son los datos que dimos en la introducción al tipo float. Tipo float Tamaño Rango 32 bits 2.93873 · 10-39 a 1,70141 · 10+38 Precisión 6 decimales A continuación vemos el resultado que ofrece el ejemplo 6 para una declaración de variable x=2/3.0 y mostrar el resultado. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 7 Tipos de datos double El tipo double se utiliza para declarar datos en formato de coma flotante de doble precisión según la norma IEEE 754, que emplea 8 bytes (64 bits) para almacenar una variable, de los que 53 son para la mantisa (1 para el signo y 52 para el valor) y 11 para el exponente (1 para el signo y 10 para el valor). Por omisión una constante es considerada de tipo double, por lo que si no necesitamos tanta precisión debemos tenerlo en cuenta para no ‘gastar’ recursos y tiempo del sistema inútilmente. La precisión será: 252 = 4 503 599 627 370 496 Vemos que el resultado tiene 16 cifras, lo que indica que se pueden representar todos los números de 15 o menos cifras y de 16 cifras hasta ese límite. Es decir, la precisión de un float está, dependiendo del número, entre 15 y 16 cifras no pudiendo representarse números mayores al resultado obtenido. Por otro lado, para el rango diremos que con 10 bits el número más grande que se puede representar es 210= 1024), por lo que el rango superior será: 21024 = 1,797693 · 10+308 El número más pequeño, en valor absoluto, será: 2-1024 = 5,562684 · 10-309 También se pueden declarar variables de tipo long double, aunque ANSI C no garantiza ni un rango ni una precisión mayores que las de double. Tipo double long double Tamaño Rango Precisión 64 bits 80 bits 5,562684 · 10-309 a 1,797693 · 10+308 3.4 x 10-4932 a 1.1 x 10+4932 15 decimales A continuación vemos ejemplos de declaración de variables de este tipo. double x = 3.141592; double y = 2.2e-6; //2.2e-6 = 2.2 por 10 elevado a -6 double m = 2/3.0; Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 8 Tipos de datos double Ejemplo 7.- Datos de tipo double Se trata de realizar un ejemplo similar al 6 pero declarando variables de tipo double. bool Son variables de tipo Booleano que solo pueden tomar uno de los dos valores posibles, verdadero (true) o falso (false). Las palabras clave true y false son literales Booleanos con valores predefinidos. Numéricamente false es cero y true es uno. Este tipo de variables son especialmente útiles para almacenar respuestas del tipo si o no para almacenar información sobre cualquier variable que solamente puede tomar dos valores. Podemos convertir un valor de tipo bool es uno de tipo int. La conversión numérica establece la condición de falso a cero y la de verdadero a uno. void Se trata de un tipo especial que indica precisamente la ausencia de tipo. Su uso resulta obligatorio en C, utilizándose principalmente en funciones que no devuelven ningún valor o que no requieren parámetros y en punteros genéricos (lo estudiaremos posteriormente). Aunque una función que no devuelve ningún valor parece una contradicción no lo es, simplemente hacen su trabajo (por ejemplo borrar la pantalla) y no devuelven valores (en borrar la pantalla no hay que devolver ningún valor y no requiere ninguna entrada). Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 9 Tipos de datos Ejemplo 8.- Error overflow con tipo int Se trata de realizar un ejemplo para demostrar y explicar que ocurre cuando realizamos una operación con un tipo int que hace que se supere el rango del tipo. Recordemos el rango de int: Tipo Tamaño Rango int (Abreviatura de signed int) 32 bits -2147483648 a 2147483647 unsigned int 32 bits 0 a 4294967295 Si hacemos una declaración como: int x = 2147483647 (límite superior del int y le sumamos 0 el resultado será 2147483647, pero que ocurre si le sumamos 1, que provoca un overflow, pues que con un código como el siguiente: // Error overflow con tipo int #include <stdio.h > int main() { int x = 2147483647,sumar0=0,sumar1=0; printf(" Error overflow con tipo int"); sumar0 = x + 0; sumar1 = x + 1; printf("\nResultado obtenido al sumar 0: "); printf("%I32d", sumar0); printf("\nResultado obtenido al sumar 1: "); printf("%I32d", sumar1); return 0; } Obtenemos el siguiente resultado. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 10 Tipos de datos Vemos que se comete un error sin que el compilador nos de ningún tipo de mensaje, es decir, cometemos un error de programación que no detecta el compilador, pero si en cambio nos da un resultado muy diferente al esperado aunque si es un resultado congruente. ¿Qué está ocurriendo?. Para aclararlo debemos descender a realizar operaciones a nivel de microprocesador, es decir, a nivel de bit, para lo que vamos a analizar en binario la operación que se realiza. Decimal 2147483647 Binario Signo 0 Byte 3 (MSB) - 7 bits Byte 2 - 8 bits 1111111 11111111 Byte 1 - 8 bits 11111111 Sumamos una unidad al número original expresado en binario 2147483648 1 0000000 00000000 00000000 Byte 0 (LSB) - 8 bits 11111111 +1 00000000 Vemos que el procesador efectúa la operación en binario, obtiene un resultado (2147483648) que está dentro del rango de declaración de la variable int (-2147483648 a 2147483647) y finalmente interpreta que un 1 en el bit más significativo del Byte 3 (signo) origina un resultado negativo, luego, desde el punto de vista del procesador, el resultado es totalmente congruente, aunque desde el punto de vista matemático no lo sea. Podemos sacar como conclusión de este análisis que el resultado depende del procesador y que debemos tener especial cuidado con los rangos de declaración de variables tanto enteras como de otros tipos, para no cometer errores graves de programación. Si en lugar de sumar 1 a la variable x sumamos otros valores seguimos obteniendo resultados que están dentro de rango, como vemos seguidamente: Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 11 Ámbito de una variable Hemos visto que el tipo de una variable hace referencia a la naturaleza de la información que contiene, como los tipos char, int, long, float, double, etc. El ámbito o modo en que se almacena es la característica de las variables de lenguaje C que determina cuándo se crea una variable, cuándo se destruye (deja de existir) y desde que parte del programa se puede acceder a ella. Veamos los modos de esta característica. Modo auto o automático. Esta será la opción por defecto para las variables declaradas dentro de un bloque de programa (conjunto de sentencias entre llaves). En lenguaje C la declaración debemos hacerla siempre al comienzo del bloque y en C++ la declaración puede estar en cualquier lugar. En la figura vemos el error que se produce con una declaración de variables fuera de lugar. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 12 Ámbito de una variable Modo auto o automático. La palabra auto no tenemos que explicitarla, tan solo se usa para la definición. Una variable auto se crea cuando comienza la ejecución del bloque de código y se destruye cuando finaliza este bloque, ocurriendo esto cada vez que el bloque se ejecuta. El ámbito de estas variables es local, o sea, solamente están disponibles para ese bloque de programa y no se inicializan por defecto, por lo que debemos tener precaución con los valores aleatorios que pueden tomar, siendo lo más correcto siempre que se declaran inicializarlas. Veamos un ejemplo: { int x=0, y=1; //Declaramos e inicializamos dentro del bloque negro x e y Sentencias { double n=10, y=10.; /*Declaramos e inicializamos dentro del bloque marrón n y otra y La nueva declaración de y oculta a la y de tipo int */ Sentencias y=y+n; /* Realizamos una suma entre a e y de tipo float La variable y de tipo int no es visible en este bloque pero si lo es la variable x */ } /* Fuera del bloque marrón la variable n ya no existe e y es de tipo int La variable y si es visible, vale 1 y es de tipo int */ } Modo extern o externo. Tienen un ámbito global y se definen fuera de cualquier bloque o función, por ejemplo antes de la función main(). Este ámbito global indica que son variables que existen durante toda la ejecución del programa y que están disponibles para todas las funciones definidas después de su declaración y hasta el fin del fichero. Si queremos acceder a estas variables desde funciones definidas antes de su declaración o desde otros ficheros, debemos declararlas en estos precedidas de la palabra extern. Si cuando declaramos una variable global no la inicializamos esta toma por defecto el valor cero. Aunque podemos declarar variables de tipo auto en un bloque de programación con el mismo nombre que una de tipo extern no es una práctica recomendable porque podemos perder el control de su valor si no tenemos mucho cuidado. NO SE RECOMIENDA DECLARAR VARIABLES CON EL MISMO NOMBRE Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 13 Ámbito de una variable Modo static o variables estáticas. Se trata de un tipo parecido al auto pero que hace que las variables así declaradas conserven su valor entre distintas ejecuciones del bloque de programación, es decir que permanecen en memoria durante toda la ejecución del programa. Cuando llamamos varias veces a una variable static el valor de esa variable se conserva entre llamada, es decir que la variable se inicializa solamente la primera vez que sea utilizada. Si no la inicializamos al declararla se pone por defecto a cero. Congruencia y conversión entre tipos Es evidente que para poder realizar operaciones aritméticas entre variables estas tienen que ser del mismo tipo. Si, por ejemplo, tenemos una de tipo int y otra tipo float, la primera se convierte a float, o dicho de otra forma, antes de efectuarse la operación la de menor rango convierte el tipo para que su rango sea el de la de mayor rango. Se trata de una conversión o adaptación de tipos que se realiza de forma automática por parte del compilador sin necesidad de intervención alguna por parte del programador. Ante situaciones de este tipo, y aunque no tengamos que intervenir, si es necesario conocer las reglas con las que se producen estos cambios. Los tipos vistos hasta ahora se ordenan por rangos de mayor a menor de la siguiente forma: long double > double > float > unsigned long > long > unsigned int > int > char Esta conversión también se produce cuando el resultado de una expresión es asignado a una variable convirtiéndose dicho resultado al tipo de la variable. En estas conversiones debemos tener mucho cuidado porque si, por ejemplo, la variable a la que se asigna el resultado es de menor rango que el de la expresión perderemos información. Es decir, en una operación como la siguiente: double a = 35.4,b = 12.0; int y; y = a*b-3*b+7; Cabría esperar que el valor final de y fuese 395,8 y en cambio es de 395. Otro tipo de conversión es la que se realiza con el operador cast o explicita, que es una conversión de tipo, forzada por el programador. El operador cast consiste en preceder la constante, variable o expresión que se desea convertir por el tipo al que se desea convertir, encerrado entre paréntesis. Veamos un ejemplo: Vf = (int) 3.5 + ((int) I * (int) R); Tanto las variables I y R sean del tipo que sean, como la constante 3.5, de tipo double, se convierten a tipo int. El operador cast se utiliza normalmente a valores de retorno de funciones y expresiones para asegurar el tipo. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 14 Tipos de datos constantes Una constante es una información que no cambia durante la ejecución de un programa y puede ser numérica o alfanumérica. La única forma de cambiar el valor de una constante es mediante una nueva compilación. El uso de constantes permite modificar más fácilmente un programa ante posibles cambios de esa constante. Por ejemplo, supongamos que un programa utiliza n veces la constante PI que hemos definido como const double PI = 3.141592 y posteriormente necesitamos cambiar el valor de dicha constante a 3,1415926536, solo tendremos que modificar la línea que define la constante. En lenguaje C existen varios tipos de constantes que veremos seguidamente. Enteras. Una constante entera decimal es un conjunto de dígitos del 0 al 9 que forman un número entero y quedan sujetas a las restricciones de rango que tienen las variables tipo int y long. Naturalmente también pueden ser con y sin signo. El tipo de una constante depende de de forma genérica de su magnitud, o bien por la postposición de ciertos caracteres, como vemos en los ejemplos que siguen. 31204 //constante de tipo int 32790 //constante de tipo long (es mayor que límite superior de short - 32767) 333u // constante de tipo unsigned int que también se puede escribir 333U 1230l //constante tipo long que también se puede escribir 1230L 455ul //constante tipo unsigned long que también se puede escribir 455UL 077 //constante octal por ir precedida de 0 (63 en decimal) 77 //constante entera decimal (ojo que no es igual a 077) 0xA //constante haxadecimal por ir precedida de 0x o 0X (10 en decimal) 0XFB //constante hexadecimal que equivale a 251 en decimal De coma flotante o tipo float. Constantes que pueden ser de tipo float, double y long double. Si no especificamos nada las constantes de coma flotante son de tipo double. Indicaremos que una constante de como flotante es float añadiendo una f minúscula o F mayúscula y para indicar que es de tipo long double, se le añade una l o una L, pero en cualquier caso siempre deberemos poner el punto decimal. Se pueden expresar como cadenas de dígitos de 0 a 9 con punto decimal o bien notación científica incluyendo siempre el punto decimal en la parte entera y expresando la parte fraccionaria con e o E y un exponente entero con o sin signo. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 15 Tipos de datos constantes Veamos algunos ejemplos de constantes en coma flotante. 1.4142 //constante de tipo double 54.6693f //constante de tipo float 567f //constante de tipo float MAL declarada porque falta el punto decimal 0.0001 //constante de tipo double que se puede expresar por 0.0001 10e3 //constante de tipo double que equivale a 10x103=10000 .1e-3 //constante de tipo double en notación científica que equivale a 0.1x10-3 = 0,0001 3.33E-2f //constante de tipo float expresada en notación científica Carácter. Una constante de este tipo es cualquier carácter encerrado entre comillas simples, como por ejemplo 'a' o 'b'). El valor de una constante carácter es su valor numérico según el código ASCII. Ciertos caracteres como la comilla simple ('), la barra invertida (\) y otros, se representan mediante las denominadas secuencias de escape (constituidas por la barra invertida (\) seguida de otro carácter y cuyo fin es cambiar el significado normal del carácter que sigue a la barra invertida) que vemos en la tabla. Constante Lenguaje C ASCII Significado BEL \a 7 Sonido o alerta NL \n 10 Salto de línea HT \t 9 Tabulación horizontal BS \b 8 Retroceso CR \r 13 Retorno de carro FF \f 12 Salto de página \ \\ 92 Barra invertida o contrabarra ' \' 39 Comilla simple “ \” 34 Comillas dobles NULL \0 0 No operación o carácter nulo Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 16 Tipos de datos constantes Cadenas de caracteres. Una cadena de caracteres es un conjunto de caracteres encerrados entre comillas dobles ("). Por ejemplo: "Este conjunto de caracteres es una cadena" Dentro de la cadena, podemos poner caracteres en blanco y secuencias de escape como las vistas. Si queremos que aparezcan las propias comillas en la cadena debemos anteponerle la contrabarra para evitar que las interprete como final de la cadena. Indicar como muy importante que el compilador adiciona siempre un byte nulo (\0) al final de cada cadena de caracteres para señalar el final de la misma. Esto solamente es importante de cara al tamaño ya que, por ejemplo, la cadena "ordenador" no ocupa 9 bytes, sino 10, los nueve de la cadena más el del carácter nulo. Algunos ejemplos de cadenas de caracteres pueden ser: "Programación" "'A'" " Esta cadena contiene espacios en blanco " "Esta cadena encierra \”otra cadena\” entre comillas y provoca un salto de línea.\n" Tipo enumerado (enum). Son una clase especial de constantes de C que consisten en una lista de valores constantes de tipo int. Para crear una enumeración debemos definir un nuevo tipo de datos, denominado tipo enumerado y de declarar una variable de este tipo. Su sintaxis es la siguiente: enum [<identificador de tipo_enumerado>] { <nombre de la constante> [ = <valor>], ... } [lista de variables]; El identificador de tipo es opcional y nos permitirá declarar más variables de tipo enumerado: [enum] <identificador de tipo_enumerado> <variable1> [,<variable2> [...] ]; La lista de variables es opcional y se puede definir separada por comas en una o en varias lineas. Vamos a ver un ejemplo típico de enumeración que es el de las horas que marca un reloj. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 17 Tipos de datos constantes enum TipoDeHoras { una = 1, dos, tres, cuatro, cinco, seis, siete, ocho, nueve, diez, once, doce, trece = 1, catorce, quince, dieciseis, diecisiete, dieciocho, diecinueve, veinte, veintiuna, veintidos, veintitres, veinticuatro = 0 }; Vemos que las horas una y trece valen 1, dos y catorce valen 2, etc.; y que veinticuatro vale 0. Vemos en el ejemplo que una vez que se asigna un valor a un elemento de la lista los demás toman valores correlativos. En caso de no asignar ningún valor, el primer elemento toma el valor 0. Otro ejemplo típico de enumeración es el de los días de la semana, como vemos en el ejemplo siguiente. enum DiasSemana { lunes, martes, miercoles, jueves, viernes, sabado, domingo } hoy; // declara la variable hoy del tipo enumerado DiasSemana enum DiasSemana ayer; // declara la variable ayer del tipo enumerado DiasSemana ayer = lunes; // asigna el valor lunes a la variable ayer del tipo enumerado DiasSemana ayer =0; // equivalente a la asignación ayer = lunes Esta última línea cumple las especificaciones para ANSI C, en cambio para C++ no resulta valida la asignación de un valor int a una variable de tipo enumerado, sino que hay que hacer una conversión explicita de tipo, como: ayer = (enum DiasSemana)0; // conversión explicita de int (0) a tipo enumerado DiasSemana En este ejemplo el valor de lunes es 0, de martes 1, y así sucesivamente. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 18 Tipos de datos constantes El modificador const. Al utilizar el modificador const cuando declaramos una variable en realidad estamos indicando que esa variable no puede cambiar de valor. Por ejemplo: const int i=10; //hacemos que la variable i sea una constante const int matriz[] = {1, 2, 3, 4}; //declaramos una matriz tipo int que se hace constante El lenguaje C no define lo que ocurre si en otra parte del programa o en tiempo de ejecución se intenta modificar una variable declarada como const aunque se originará una mensaje de error. Otra forma de declarar una constante es utilizando la directriz #define, normalmente antes de la función main, y lo que se hace es indicar la compilador el nombre de la constante y su valor. Su sintaxis es la siguiente: #define nombre <valor> Seguidamente se ven ejemplos de declaración de constantes: #define SALUDO "¡ Hola, que tengas un buen día !" #define PI 3.141592 Debemos observar que después de la declaración no se pone punto y coma. Esto viene motivado porque una directriz u orden al preprocesador no es una sentencia de C. Es practica habitual escribir el nombre de las constantes en mayúsculas. Seguidamente vemos algunos ejemplos basados en la ayuda de C++ Builder: const float PI = 3.14; const MAXIMO = 12345; // Si se omite el tipo, se supone int char *const CADENA1 = "Hola mundo"; // Un puntero constante char const *CADENA2 = "Hola"; // Un puntero a una cadena de caracteres constante Una vez realizadas las declaraciones anteriores, las siguientes provocarán errores: PI = 3.0; // Asigna un valor a una constante i = MAXIMO++; // Incrementa una constante CADENA1 = "Hola, ¿que tal?"; // Puntero CADENA1 señala a más de un lugar Algunos de los términos usados se describirán con posterioridad Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 19 Operadores Un operador es un símbolo especial que indica al compilador que debe efectuar una operación matemática o lógica. En C existen muchos operadores de diversos tipos que vamos a describir a continuación. Operadores Aritméticos. Los operadores aritméticos son sencillos de entender y de utilizar. En C se utilizan los operadores siguientes: Operador + * / % ++ -- Ope ración Suma Resta M ultiplicación Div isión Resto o módulo Incre me nto Decreme nto Obse rvaciones Ope radores enteros o coma flotante Calcula el re sto de una división e ntera. Operadore s ente ros. Suma 1 al v alor de la expresión. Resta 1 al v alor de la expresión. Todos estos operadores se pueden aplicar a constantes, variables y expresiones. El resultado es el que se obtiene de aplicar la operación correspondiente entre los dos operandos. El operador % se aplica solamente a constantes, variables o expresiones de tipo int y su significado lo vemos diciendo que el resultado de la operación 10%3 es 1 ya que el resto de dividir 10 entre 3 es 1. Evidentemente nos resultará muy útil para saber si un número es múltiplo de otro averiguando si el resto es cero. Una expresión es un conjunto de variables y constantes, como por ejemplo el siguiente polinomio: x*x/3.0-5.0*x+3.0 Las expresiones pueden contener paréntesis para agrupar términos. Puede haber paréntesis contenidos dentro de otros paréntesis. El significado de los paréntesis coincide con el habitual en las expresiones matemáticas. Los operadores incrementales pueden ir inmediatamente delante o detrás de la variable. Si preceden a la variable, ésta es incrementada antes de que el valor de dicha variable sea utilizado en la expresión en la que aparece. Si es la variable la que precede al operador, la variable es incrementada después de ser utilizada en la expresión. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 20 Operadores Ejemplos de código de programa: int a = 10, b = 3, c; float x=2.0, y; y = x + a; // Resultado 12.0 (tipo float) c = a / b; // Resultado 3 (tipo int) c = a % b; // Resultado 1 (tipo int) y = a / b; // Resultado 3 (tipo int). Se convierte a float para asignarlo a y c = x / y; // Resultado 0.66667 (tipo float). Se convierte a int para asignarlo a c de tipo int (c = 0) a = a++; //Postincremento: Evaluá la expresión y se incrementa en 1 a = ++a; //Preincremento: Se incrementa en 1 y después se evaluá la expresión a = a- -; //Postincremento: Evaluá la expresión y se decrementa en 1 a = - -a; //Preincremento: Se decrementa en 1 y después se evalúa la expresión Los operadores incrementales son muy utilizados por lo que es importante entender muy bien por qué los resultados m y n del ejemplo siguiente son diferentes. int i = 1, j=1,x,y; x = i++; // después de ejecutarse esta sentencia x=1 e i=2 y = ++j; // después de ejecutarse esta sentencia y=2 y j=2 Operadores de Asignación. Los operadores de asignación atribuyen a una variable (escriben en la zona de memoria asignada a la variable) el resultado de una expresión o el valor de otra variable. El operador de asignación más usado es la igualdad (que no debemos confundir con la igualdad matemática). Su sintaxis general es: NombreVariable = expresion; Se evalúa expresion y el resultado se deposita en NombreVariable, sustituyendo cualquier otro valor que hubiera en esa posición de memoria. Una posible utilización de este operador es: variable = variable + 1; Desde un punto de vista matemático esto no tiene sentido, pero sí lo tiene considerando que en realidad el operador de asignación representa una sustitución; es decir, se toma el valor de variable que tenemos en memoria, se le suma uno y el valor resultante vuelve a guardarse en memoria. El resultado ha sido incrementar el valor de variable en una unidad. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 21 Operadores Ope rador = *= /= %= += -= <<= >>= &= ^= |= Ope ración Asignación simple M ultiplicación más asignación Div isión más asignación Re sto más asignación Suma más asignación Re sta más asignación De splazamie nto izquie rda más asignación De splazamie nto de re cha más asignación AND sobre bits más asignación XOR sobre bits más asignación OR sobre bits más asignación Obse rv acione s La e xpre sión: E1 ope rador= E2 Tie ne e l mismo e fe cto que E1 = E1 ope rador E2 Por e je mplo: E1 += E2 e s lo mismo que E1 = E1 + E2. Ejemplos de código de programa: int x = 5, n = 10, i = 1; i += 2; // Realiza la operación i = i + 2. Resultado i = 1 + 2 = 3 x *= n - 3; // Realiza la operación x * (n - 3) y no x * n – 3 y el resultado es x = 5 * (10 - 3) = 35 n >>= 1; // Realiza la operación n = n >> 1 es decir, desplaza el contenido de n 1 bit a la derecha Operadores Relacionales. Dan al lenguaje de programación la posibilidad de considerar alternativas, es decir proceder de una forma u otra según se cumplan o no ciertas condiciones analizando si se cumplen o no esas condiciones. Nosotros estamos acostumbrados a evaluar este tipo de condiciones en el lenguaje natural evaluando condiciones usando palabras como si (yes), no (no), conectado (on), desconectado (off), verdadero (true), falso (false) y otras muchas más. En informática se ha adoptado la forma true-false obteniendo un resultado de evaluación de una condición true si esta se cumple y false en caso contrario. En lenguaje C la condición false se representa por un 0 y cualquier otro valor representa true, aunque normalmente se adopta un 1 para true. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 22 Operadores Ope rador < > <= >= != Pre gunta Pre gunta Pre gunta Pre gunta Pre gunta si si si si si el el el el el prime r prime r prime r prime r prime r Ope ración ope rador e s me nor que e l se gundo. ope rador e s mayor que e l se gundo. ope rador e s me nor o igual que e l se gundo. ope rador e s mayor o igual que e l se gundo. ope rador e s distinto de l se gundo. Ejemplos de código de programa: int a = 10, b = 0, r = 0; r = a ==b; // r=0 (falso) porque 10 no es igual a 0 r = a > b; // r=1 (cierto) porque 10 es mayor que 0 r = a != b; // r=1 (cierto) porque 10 es distinto de 0 Operadores Lógicos. Son operadores binarios que permiten comprobar que se cumplen simultáneamente varias condiciones, que se cumple una u otra, etc. En lenguaje C los operadores lógicos responden a las funciones del álgebra de Boole AND (&&), OR (||) y NOT (!). El resultado de efectuar una operación lógica siempre será un valor lógico verdadero o falso (0 o 1). Es decir, toda expresión numérica con valor distintos de 0 origina un resultado verdadero y cuando la expresión numérica toma valor 0 origina un resultado falso. Ope rador && || (ASCII 124) ! Ope ración AND lógica OR lógica NOT lógica Obse rv acione s El re sultado e s falso si al e v aluar su ope rando e l re sultado e s v e rdade ro Si al e v aluar cada ope rando e l re sultado e s v e rdade ro da como re sultado v e rdade ro. Si uno de e llos e s falso, e l re sultado e s falso. Si e l prime r ope rando e s falso, e l se gundo no se e v alúa. Si al e v aluar cada ope rando e l re sultado e s falso da como re sultado falso. Si uno de los ope rando e s v e rdade ro da como re sultado v e rdade ro. Si e l prime r ope rando e s v e rdade ro no se e v alúa e l se gundo. El re sultado e s falso si al e v aluar su ope rando e l re sultado e s v e rdade ro y v e rdade ro e n caso contrario. Ejemplos de código de programa: int a = 10, b = 0, c = 0; c = (a != 0) && (b != 0); //(1) Resultado c = 0 (falso). El operando de (a != 0) = 1 (verdadero) y el operando de (b ! = 0) = 0 (falso) y un AND entre verdadero y falso da como resultado falso. c = (a != 0) || (b > 0); // (2) Resultado c = 1 (verdadero). (a != 0) = 1 y (b > 0) = 0, luego 1 || 0 = 1. c = !c; // Si tomamos el valor de (1) c = 1 y si tomamos el valor obtenido en (2) c = 0 Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 23 Operadores Operadores Bitwise (con referencia a Bit). Operador & Operación AND a nivel de bits | OR a nivel de bits (ASCII 124) ^ XOR a nivel de bits ~ Complemento a 1 (ASCII 126) Observaciones Compara dos bits generando un resultado 1 si ambos son uno, en caso contrario el resultado es 0. Compara dos bits generando un resultado 1 si ambos o cualquiera de ellos es 1, en caso contrario el resultado es 0. Compara dos bits generando un resultado 1 si los bits son complementarios, en caso contrario el resultado es 0. Realiza el complemento a 1 (cambia 1 por 0 y 0 por 1) del operando. >> Descarta el bit menos significativo. Si es un número sin signo Desplazamiento a la pone a 0 el bit más significativo, caso contrario desplaza el bit de derecha signo por la izquierda. << Desplazamiento a la Descarta el bit más significativo y rellena con 0 por la derecha. izquierda - Cambia el signo del operando Calcula el complemento a 2 (complemento a 1 mas 1) del operando. El operando puede ser entero o real. + Complemento del operador menos Se puede anteponer a una variable o expresión, pero en realidad no hace nada. Ejemplos de código de programa: int a = 255, r1 = 0, r2 = 0,r3 = 0,r4 = 0,r5 = 0,r6 = 0,r7 = 0,r8 = 0,m = 32; r1 = a & 17; // 11111111 & 00010001 = 00010001. Hace que a tome el valor 17 r2 = a & 15; // 11111111 & 00001111 = 00001111. Pone a 0 todos los bits de a excepto los 4 de menor peso r3 = r3 | m; // 00000000 | 00100000 = 00100000. Hace r = m (32) r4 = -a; // Resultado r = -255 r5 = a & ~7; // 11111111 & 11111000 = 11111000=248 (7 = 00000111 y ~07=11111000) r6 = a >> 7; //Desplaza 7 bits a la derecha con lo que r=00000001=1 r7 = m << 1; // 32=00100000 luego si desplazamos un bit a la izquierda r = 01000000=64 r8 = m >> 1; // 32=00100000 luego si desplazamos un bit a la derecha r = 00010000=16 Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 24 Operadores Operador sizeof(). El operador sizeof() da como resultado el tamaño en bytes de su operando, pudiendo ser el operando el identificador o el tipo de variable que debemos declarar previamente, y como este tamaño depende del compilador y del ordenador resulta un operador necesario para producir código portable. Aunque por los paréntesis pueda parecer una función se trata de un operador. Veamos como usarlo: int a=0, t=0; t = sizeof a; // No son necesarios paréntesis, aunque podemos usarlos sizeof(int); // El operador trabaja con un tipo de datos y los paréntesis son imprescindibles Ejemplo 9.- Uso del operador sizeof() Se trata de realizar una aplicación de consola que devuelva el tamaño de datos de tipo entero para obtener algo similar a lo siguiente: Desarrollar la aplicación anterior para ver el tamaño de datos de los tipos vistos hasta el momento, es decir: char, short, long, float, double, bool y void estableciendo los datos con y sin signo. Operador coma. La coma (operador coma) se emplea para encadenar varias operaciones que se ejecutarán de izquierda a derecha. Su forma general es: expresion = exp1, exp2 Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 25 Operadores Operador condicional. Tiene la sintaxis siguiente: PrimerOperando ? SegundoOperando : TercerOperando La expresión PrimerOperando debe ser una expresión booleana y la ejecución de ?: se realiza así: Se evaluá PrimerOperando y si el resultado es verdadero el resultado de la expresión condicional es SegundoOperando. Si el resultado de evaluar PrimerOperando es falso, el resultado de la expresión condicional es TercerOperando. El resultado de: double a = 10.2, b=20.5,mayor=0; mayor=(a>b)?a:b; printf("a = %.11lf b=%.11lf luego mayor = %.11lf\n\n",a,b,mayor); Será algo parecido a: Operadores de expresión (Prefijos). Ope rador { } Obse rv acione s Se usan para agrupar e xpre sione s, aíslar e xpre sione s condicionale s, indicar llamadas y paráme tros de función. Se usan para indicar e l principio y e l final de de claracione s compue stas [ ] Se usan para indicar subíndice s de matrice s o arrays ( ) . - > Se usan para te ne r acce so a e structuras y para la unión de mie mbros Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 26 Operadores Operadores dirección (&) e indirección (*) de datos. Puesto que se trata de operadores los vamos a introducir en este momento e indicaremos que se trata de uno de los aspectos más potentes del lenguaje, aunque bastante complicado de dominar. La sintaxis general de estos operadores es: *Expresion; &NombreVariable; El operador & se denomina de dirección y lo que hace es devolver la dirección de memoria donde reside la variable, en el ejemplo NombreVariable. Por ejemplo, una vez ejecutada la siguiente sentencia: NVar1 = &Nvar2; NVar1 contiene la dirección de memoria donde se guarda el contenido de NVar2. Las variables que almacenan direcciones de otras variables se denominan punteros, y deben ser declaradas como tales, teniendo su propia sintaxis y forma de funcionamiento que veremos en otro tema. MUY IMPORTANTE: La dirección de memoria de una variable no la podemos modificar, es el sistema operativo el encargado de asignarla, por lo que no podemos realizar operaciones del tipo: &VarX = DireccionDeMemoria; //MAL El operador * se denomina de indirección siendo el complementario del operador de dirección &, es decir devuelve el contenido de una determinada dirección de memoria. Por ejemplo, en la siguiente sentencia NVar3 = *NVar1; el puntero Nvar1 (contenido de la dirección de memoria representada por la variable Nvar1) se asigna a la variable NVar3. Operaciones o expresiones y sus tipos Una expresión es la combinación de variables, constantes (opcionalmente) y operadores, dando como resultado el obtenido de aplicar los operadores a los operandos; es decir, que cuando aparece una expresión en un programa, una vez se ejecuta, es evaluada y sustituida por su resultado. Una expresión puede estar formada por otras expresiones más sencillas, y puede contener paréntesis de varios niveles agrupando distintos términos. En C existen distintos tipos de expresiones que vamos a estudiar seguidamente. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 27 Operaciones o expresiones y sus tipos Aritméticas. Se forman con variables, constantes y operadores aritméticos e incrementales (+, -, *, /, %, ++, --) pudiendo usar paréntesis en tantos niveles como resulte necesario. Por ejemplo, la solución de una ecuación de segundo grado completa se da matemáticamente por: −b± b 2−4⋅a⋅c −b b2 −4⋅a⋅c −b− b 2−4⋅a⋅c x= ; x 1= y x 2= 2⋅a 2⋅a 2⋅a En C debemos escribir, por ejemplo la primera solución con la siguiente sentencia: x1 = (-b + sqrt ((b*b) – (4 * a * c)))/(2*a); En esta sentencia todo lo que está a la derecha del operador de asignación (=) es la expresión aritmética; lo que está a la izquierda del signo (=) es una variable, y el ; es la finalización de sentencia obligatorio en C. En la expresión anterior aparece la función sqrt(), que tiene como valor de retorno la raíz cuadrada de su único argumento. Lógicas. Deben formarse con los valores lógicos verdaderos (true = 1) y falsos (false = 0) y los operadores lógicos ||, && y ! pudiéndose emplear operadores relacionales (<, >, <=, >=, ==, !=) para obtener los lógicos partiendo de valores numéricos. Las expresiones lógicas dan siempre como resultado un valor true o false. Por ejemplo: Var = ((a >= b) && (b >= c)) || ((a >= b) && (b == d)); La variable Var será true si a es mayor o igual que b Y (AND) b es mayor o igual que c O (OR) a es mayor o igual que b Y (AND) b es igual que d. Generales. Una de las características más importantes y potentes del lenguaje C es su flexibilidad para combinar expresiones y operadores de distintos tipos en una expresión que denominaremos general. Como el resultado de una expresión lógica es un valor numérico (1 ó 0) se pueden combinar expresiones lógicas como parte de una expresión aritmética y también cualquier valor numérico puede ser considerado como un valor lógico (true si es distinto de 0 y false si es igual a 0). Por ejemplo: (Var_a - Var_b*10.0) && (Var_c != Var_d) Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 28 Jerarquía de operaciones Las operaciones se efectúan en un orden jerárquico que si no se tiene en cuenta al plantear y resolver una determinada operación casi siempre conduce a resultados equivocados o no deseados como estos: • Consideremos la expresión 2 + 3 * 4. Si realizamos primero la suma (2+3=5) y después el producto (5*4), el resultado es 20, pero si se realiza primero el producto (3*4=12) y luego la suma (2+12), el resultado es 14. Para que el el resultado de cada expresión sea claro e inequívoco, es necesario definir las reglas que definen el orden con el que se ejecutan las expresiones en lenguaje C. Evidentemente que en matemáticas 2 +3 * 4 = 20 que es un resultado incorrecto. • Si calificacion1=6 y calificacion2=8 y usamos en un programa media=calificacion1 + calificacion2/2 podemos obtener dos posibles resultados 10 y 7 según el orden de evaluación del operador. Antes de plantear una formula en un programa se debemos evaluar el siguiente orden de operaciones: 1. Paréntesis 2. Potencias y raíces 3. Multiplicaciones y divisiones 4. Sumas y restas 5. Dos o más de la misma jerarquía u orden, entonces resolver de izquierda a derecha En caso de duda usaremos paréntesis, aunque tampoco es bueno usar paréntesis de mas en una operación, esto solo indica que no se evalúo bien la formula. Seguidamente resumimos las reglas de prioridad de mayor a menor, teniendo los operadores escritos en una misma línea la misma prioridad y sentido de ejecución que se indica. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 29 Jerarquía de operaciones Sentido de ejecución Operador ( ) [ ] . -> - ~ ! ++ (tipo) * (inidrección) & (dirección) sizeof * / % + << >> < <= > >= ↓ == != & | && || ?: = *= /= %= += -= <<= >>= &= |= , (operador coma) Sentido de ejecución → ← → → → → → → → → → ← ← → Ahora es evidente que la expresión 2 + 3 * 4 se realiza como 3*4=12 y luego la suma 2+12, ya que el producto tiene prioridad ante la suma. Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 30 Sentencias y sus tipos Las operaciones o expresiones forman parte o son componentes elementales de las entidades de rango superior denominadas sentencias, que son unidades completas, ejecutables en sí mismas. Los tipos de sentencias los vemos seguidamente. Simples. Son sentencias simples las expresiones u operaciones de cualquier tipo finalizadas con el carácter ;, como por ejemplo una sentencia de tipo aritmético como la siguiente: float numero; Resistencia = V/R; Nula. Se trata de una sentencia que no efectúa ninguna tarea pero que en si es una sentencia que ocupa su lugar. Se trata simplemente del carácter ;. Bloques se sentencias o sentencias compuestas. Se trata de cualquier conjunto de declaraciones y de sentencias agrupadas entre llaves { }. Una sentencia compuesta a su vez puede incluir otras sentencias (simples y/o compuestas). Los ejemplos vistos hasta ahora contienen bloques de sentencias y aquí reproducimos el del ejemplo 9. { int a=0, tam=0; tam = sizeof a; printf("\nEl tamaño del entero 'a' es: %d bytes\n",tam); printf("El tamaño de un entero 'int' es: %d bytes\n", sizeof(int)); printf("\n %s", "Pulse una tecla para salir"); getch(); //espera una pulsación del teclado return 0; } Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 31 a l e d Fin s e r p n ó i c a t en Tema 3.1.2.- Tipos de datos. Variables y constantes. Operadores 32