UC3M

Grado en Ing. Telemática/Sist. Audiovisuales/Sist. de Comunicaciones

Arquitectura de Sistemas

Septiembre 2017 - Enero 2018

Capítulo 2. Tipos de datos en C

Las estructuras de datos del lenguaje C son más simples que las que ofrece Java porque no existe el concepto de clase ni de objeto. C ofrece tipos de datos básicos y dos construcciones para crear datos más complejos. El control de acceso a datos que ofrece Java (métodos y campos privados, públicos y protegidos) no existe en C. Las variable son globales, locales a un fichero, o locales a un bloque de código.

2.1. Tipos de datos básicos

C ofrece tres tipos de datos básicos:

  • Números enteros definidos con la palabra clave int

  • Letras o caracteres definidos con la palabra clave char

  • Números reales o en coma flotante definidos con las palabras claves float o double

2.1.1. Enteros

Se definen con int y admiten de forma opcional dos prefijos modificadores:

  • short y long: Modifica el tamaño en bits del entero. Existen por tanto tres tipos de enteros: int, short int (que se puede abreviar como short), y long int (que se puede abreviar como long).

    El lenguaje C no define tamaños fijos para sus tipos de datos básicos. Lo único que garantiza es que un short int tiene un tamaño menor o igual que un int y este a su vez un tamaño menor o igual a un long int. Esta característica del lenguaje ha complicado la creación de programas que sean compatibles entre varias plataformas.

  • unsigned: define un número natural (mayor o igual a cero).

Sugerencia

En tu entorno de desarrollo crea un fichero de texto con la siguiente estructura (puedes simplemente copiar y pegar el texto del siguiente cuadro):

int main() 
{

}

Inserta en la función main varias definiciones de enteros para probar todas las combinaciones posibles (hasta diez). Para comprobar que la sintaxis utilizada es correcta abre una ventana con el intérprete de comandos y, en la carpeta donde se encuentra el fichero creado, ejecuta el comando gcc -Wall -o programa fichero.c reemplazando fichero.c por el nombre del fichero que has creado. Si el comando no imprime mensaje alguno por pantalla, tu programa es correcto. Verás que el compilador genera un fichero con extensión .o, puedes borrarlo.

Preguntas de autoevaluación

Responde a las siguientes preguntas (comprueba tus respuestas también compilando el programa):

  1. El siguiente programa compila sin errores:

    void main()
    {
      int i;
      long int j;
      long k;
      short int l;
      short m;
    }
    • Verdadero

    • Falso

  2. El siguiente programa produce un error al compilar porque se está asignando una variable entera a una sin signo (natural).

    void main()
    {
      unsigned i;
      int j = 0;
    
      i = j;
    }
    • Verdadero

    • Falso

  3. La declaración unsigned short x es idéntica a short unsigned x.

    • Verdadero

    • Falso

    Escribe un breve programa y comprueba la respuesta con el compilador.

2.1.2. Letras y cadenas

Las variables de tipo letra se declaran como char. Para referirse a una letra se rodea de comillas simples: 'M'. Como las letras se representan internamente como números, el lenguaje C permite realizar operaciones aritméticas como 'M' + 25.

Las cadenas de texto o strings son simplemente tablas de char. Las funciones de biblioteca para manipular estas cadenas asumen que el último byte tiene valor cero. Las cadenas de texto se escriben en el programa rodeadas de dobles comillas y contienen el valor cero al final. A continuación se muestran dos definiciones:

#define SIZE 6
char a = 'A';
char b[SIZE] = "hello";

¿Por qué la segunda definición es una tabla de seis elementos si la palabra tiene sólo cinco letras?

Sugerencia

Reutiliza el programa de la sección anterior y añade definiciones de letras y cadenas. Para estas últimas prueba a poner diferentes tamaños de tabla (demasiado pequeños y demasiado grandes para la cadena). Escribe también expresiones aritméticas sobre las letras. Recuerda que si el compilador no emite mensaje alguno, el programa es correcto.

Preguntas de autoevaluación

  1. Considera la siguiente declaración:

    #define SIZE 6      
    char m[SIZE] = 'strag';

    Es incorrecta porque la cadena debe ir rodeada de dobles comillas.

    • Verdadero

    • Falso

    Es incorrecta porque el tamaño debe ser 5 (tiene cinco letras).

    • Verdadero

    • Falso

    Si te parece que las respuestas correctas están equivocadas, escribe esa declaración en un programa y compílalo.

  2. Un programa C que imprime el resultado de la expresión 'M' + 25 es correcto e imprime una f.

    • Verdadero

    • Falso

    Te recomendamos que escribas ese programa. Para imprimir pon printf("%c\n", 'M' + 25);.

2.1.3. Números reales

Los números reales se definen con float o double. La diferencia entre ambas es la precisión que ofrece su representación interna. Hay un número infinito de reales, pero se representan con un número finito de bits. A mayor número de bits, mayor número de reales se representan, y por tanto, mayor precisión. Los reales definidos con double tienen un tamaño doble a los definidos con float. Al igual que en el caso de los enteros, el tamaño de estas representaciones varía de una plataforma a otra.

Algunas plataformas ofrecen números reales con tamaño mayor al double que se definen como long double. Los tamaños típicos para los tipos float, double y long double son 4, 8 y 12 bytes respectivamente. A continuación se muestran varias definiciones de números reales.

float a = 3.5;
double b = -5.4e-12;
long double c = 3.54e320;

Sugerencia

Añade al programa de los apartados anteriores definiciones de números reales. Prueba a definir números muy grandes o pequeños para ver la capacidad de representación de los tres tipos. Compila para ver si las definiciones son correctas.

2.1.4. Tablas

Las tablas en C son prácticamente idénticas a las de Java, con el tamaño entre corchetes a continuación del nombre. Al igual que en Java, los índices de la tabla comienzan por cero. A continuación se muestran algunos ejemplos:

#define SIZE_TABLE 100
#define  SIZE_SHORT 5
#define SIZE_LONG 3
#define SIZE_NAME 10

int table[SIZE_TABLE];
short st[SIZE_SHORT] = { 1, 2, 3, 4, 5 };
long lt[SIZE_LONG] = { 20, 30, 40};
char name[SIZE_NAME];

Los elementos de la tabla se acceden con el nombre de la tabla seguido del índice entre corchetes.

Una de las diferencias entre C y Java es que el acceso a una tabla en C no se verifica. Cuando se ejecuta un programa en Java si se accede a una tabla con un índice incorrecto, se genera una excepción de tipo ArrayIndexOutOfBounds. Estas comprobaciones no se hacen nunca en C (a no ser que se escriban explícitamente en el programa). Si se accede a una tabla con un índice incorrecto se manipulan datos en una zona de memoria incorrecta y el programa continua su ejecución.

Tras este acceso incorrecto pueden suceder dos cosas. La primera es que la memoria a la que ha accedido por error esté fuera de los límites del programa. En este caso la ejecución termina de manera abrupta y en el intérprete de comandos se muestra el mensaje segmentation fault. La otra posibilidad es que se acceda a otro lugar dentro de los datos del programa. Esta situación seguramente producirá un error cuyos síntomas sean difíciles de relacionar con el acceso incorrecto.

Tablas de múltiples dimensiones

C permite la definición de tablas de múltiples dimensiones escribiendo los diferentes tamaños rodeados de corchetes y concatenados. El acceso se realiza concatenando tantos índices como sea preciso rodeados de corchetes. Al igual que en el caso de las tablas unidimensionales, no se realiza ningún tipo de comprobación de los índices cuando se accede a un elemento. A continuación se muestra la definición de tablas de más de una dimensión.

#define MATRIX_A 100
#define MATRIX_B 30
#define COMMON_SIZE 10

int matrix[MATRIX_A][MATRIX_B];
long squarematrix[COMMON_SIZE][COMMON_SIZE];
char soup[COMMON_SIZE][COMMON_SIZE];

Sugerencia

Añade al programa de los apartados anteriores definiciones y manipulación de tablas de los diferentes tipos de datos básicos. Comprueba que son correctos sintácticamente mediante el compilador.

2.1.5. Tamaño de los tipos de datos básicos

En C, el tamaño de los tipos de datos básicos puede variar de una plataforma a otra. Esta característica está detrás de buena parte de las críticas que recibe este lenguaje, pues de ella se derivan problemas de compatibilidad (una aplicación se comporta de forma diferente cuando se ejecuta en plataformas diferentes).

A modo de ejemplo, en la siguiente tabla se incluyen los tamaños de los tipos de datos para las plataformas Linux/Intel i686.

Tabla 2.1. Tamaños de los tipos de datos en C en la plataforma Linux/Intel i686

Tipo Tamaño (bytes)
char, unsigned char1
short int, unsigned short int 2
int, unsigned int, long int, unsigned long int 4
float4
double8
long double12