1. Tipos en la memoriaYa deberíamos saber que hay varios tipos de variables en C: Esta el int, el unsigned int, el short int, el char, el float, el double...
En general, se pueden distinguir de varias formas: los tipos y los modificadores.
Tipos son: int, char, float, double...
Modificadores serían: unsigned, short, long, long long...
En general los tipos hacen referencia a QUE se guarda en la memoria, y los modificadores a COMO se guarda en la memoria.
Comencemos viendo cuales son los distintos tipos de codificación de la información. Cuando decimos codificación de la información nos referimos a como se mezclan esos 1 y 0 que la memoria puede albergar, para representar un número.
Antes hemos visto que un numero se puede expresar en binario clásico, como por ejemplo:
011001 en binario es 25 en decimal.
Si quieres saber mas sobre el binario clásico visita:
http://personales.unican.es/togoresr/lisp/BINARIO.htm y
http://es.wikipedia.org/wiki/Sistema_binarioEl problema es que con el binario clásico solo pueden expresarse números positivos (¿no lo habías pensado?). Con la necesidad de tener también en cuenta números negativos se invento otro sistema de codificación llamado Complemento a Dos.
Básicamente el complemento a dos utiliza el bit mas significativo (BMS, el de mas a la izquierda) para decir si el numero es negativo o no (1 -> negativo, 0 -> positivo).
En caso de que lo sea se invierten todos los bits y se le suma uno para obtener el valor absoluto del número. Luego a este valor habrá que añadirle el signo. Algunos ejemplos:
1101 -> BMS 1 -> Negativo -> Se invierten el resto de bits: 010 -> Se le suma uno -> 011 -> -3
0111 -> BMS 0 -> Positivo -> Se lee tal cual -> 7
Mas info:
http://es.wikipedia.org/wiki/Complemento_a_dosEste sistema nos da una ventaja: podemos usar números negativos, pero nos da también una desventaja: el numero mas grande que podemos con 4 bits ya no será el 1111
si no que ahora será el 0111 (recuerda que en complemento a dos el 1111 es un numero negativo porque el BMS es 1).
En resumen, teniendo en cuenta que usamos un numero N de bits, el sistema binario clásico nos permite expresar números mas grandes con esos bits, pero sin embargo no podríamos expresar números negativos. El complemento a dos nos permite expresar números negativos, pero sin embargo los números que podemos expresar son mas pequeños en valor absoluto que con el binario clásico. ¿Cual es mejor? Ninguno de los dos, depende de para qué se use.
Hay otro sistema de codificación que no comentaré demasiado que se llama mantisa. Se utiliza para codificar números en notación científica. Por ejemplo el 5x10^23
En este sistema se codifica por un lado el signo, por otro lado el exponente y por otro la mantisa. Es un sistema algo mas complejo que los anteriores pero nos permite codificar números muy grandes perdiendo precisión.
Con eso será suficiente sobre la codificación de la información.
Ahora bien, respecto a cómo se organiza la memoria del ordenador, antes dijimos que cada posición de memoria era 1 byte, y que este byte tenia 8 bits. Si solo tengo 8 bits quiere decir que el numero mas grande que puedo expresar en codificación binaria clasica seria el 11111111, que es el 255 (2^numero_bits - 1). En complemento a dos, el numero mas grande que puedo expresar es: 01111111, que es 127 (2^(numero_bits-1) - 1).
Puesto que 8 bits solo nos permite expresar números pequeños (como máximo 255 o 127, dependiendo de si es binario clásico o complemento a dos), la memoria puede organizarse de forma que un mismo numero se guarde en varias posiciones de memoria, combinando los bits de esta para así formar números mas grande.
Por hablar de datos en concreto y cosas reales, un int ocupa 4 posiciones de memoria, que son 4 bytes, y 4*8=32 bits. Además, un int por defecto usa el sistema complemento a dos, por lo tanto el valor mas grande que puede obtener es:
01111111111111111111111111111111 en binario -> 2147483647 en decimal. ¡Probadlo!
El caso es que nosotros podemos controlar con nuestro código fuente cuantos bytes queremos que ocupen nuestras variables. Cada tipo tiene asociado un numero de bytes, que dependen de la maquina donde se ejecuta el programa, y que podemos averiguar con sizeof(). Si colocamos entre los paréntesis de sizeof() un tipo, esta función nos devuelve el numero de tipos char que caben en ese tipo. Puede parecer lioso, pero no lo es.
Por lo general un char ocupa 1 byte. Por lo tanto, si un int ocupa 4 bytes, sizeof(int) nos devolverá un 4, que quiere decir 4 veces lo que ocupa un char, es decir: 4*1 byte= 4 bytes.
Algunos modificadores nos dejan cambiar cuanto ocupa una variable: short indica que esa variable debe ocupar menos memoria (limitando el número mas grande y mas pequeño que podemos almacenar ahí) y long y long long nos ampliaría la memoria que ocuparía dicha variable, aumentando el número máximo. Por ejemplo, mientras que un int ocupa 4 byte y el numero mas grande que almacena en complemento a dos es 2147483647, un short int ocupa 2 bytes, y el numero mas grande que almacena en complemento a dos es 32767.
Ademas de estos modificadores hay otros que indican que tipo de codificación se usará para almacenar ese número, por lo general solo dos: signed y unsigned.
signed es lo mismo que no poner nada, es decir: int y signed int es lo mismo. Simplemente quiere decir que ese numero se almacenara en complemento a dos, mientras que si colocamos un unsigned delante diremos que ese numero queremos que se codifique en binario clasico (aumentando el numero mas grande que podemos obtener, pero eliminando números negativos).
Dicho esto ya sabemos como se guardan en la memoria todos los números posibles. ¿Pero como se guardan los caracteres?
Los caracteres se guardan con una correspondencia Numero->Caracter. Es decir, se crea un convenio. Es un sistema muy simple: se dice, por ejemplo,
que el 1 será la A, el 2 será la B, el 3 será la C.. y así con el resto. Cuando se hace con todos los caracteres se crea lo que se conoce como tabla de caracteres.
La tabla que los ordenadores usan por regla general se llama tabla ASCII:
http://es.wikipedia.org/wiki/ASCIIEn la tabla ASCII, por ejemplo, se guarda el carácter 'A' como un 65 decimal, o un 01000001 en binario. Todos los caracteres se pueden representar en números positivos con 8 bits, esa es la razón de que un char solo ocupe 1 byte.
El bit mas significativo, el numero 8, siempre sobra. Por lo tanto, como el bit mas significativo siempre es 0, da lo mismo que un char sea signed u sea unsigned, puesto que si se codifica en complemento a dos el MBS siempre será 0, y por lo tanto no se invertirán los bits y los 7 bits restantes serán leídos en binario clásico.
Sabiendo ahora que la memoria solo almacena números con estos métodos, y que puedo variar el numero de bytes que ocupa cada tipo, no será muy difícil entender que aunque yo cree una variable así:
char pruebaCaracter = 'A';
En pruebaCaracter realmente hay guardados un conjunto de 0 y 1 que representan al numero decimal 65 como dijimos antes. Por lo tanto, si yo leo pruebaCaracter como un char, el procesador leera una A, si leo pruebaCaracter como un int, el procesador leera un 65 y si leo pruebaCaracter como un unsigned int, el procesador leera un 65 también (el MBS es 0).