Una estructura es un conjunto de variables que se referencian con un único nombre. Esto es muy útil cuando queramos mantener junta información relacionada. Para definir una estructura, utilizamos la palabra clave struct. Esta palabra le indica al compilador que lo que viene a continuación es una estructura. La forma general para definir una estructura es la siguiente: struct nombre_estructura { tipo variable1; tipo variable2; ... ... tipo variablei [=expresión] ... ... tipo variableN; } lista_variables_estructura; Con esta definición realmente haremos 2 cosas: por una parte, estamos definiendo una plantilla de estructuras, la cual tomará el nombre que indique el parámetro nombre_estructura, y por otra parte, estamos definiendo una serie de variables con la forma de la plantilla definida. Estas variables son las que vendrían dadas por el parámetro lista_variables_estructura. Cada campo de una estructura puede ser inicializado en la declaración mediante una expresión de la misma forma a como se hacía al declarar una variable de un tipo básico. Un ejemplo más concreto podría ser la siguiente estructura: struct Datos_Persona { char nombre[20]; char apellido1[20]; char apellido2[20]; char calle[50]; char ciudad[20] = "Sevilla"; unsigned long DNI; } pers1, pers2, pers3, pers4; Si posteriormente deseamos crear más variables utilizando la plantilla de estructura datos_persona, bastaría con declararla de la siguiente forma: struct Datos_Persona nueva_persona; Hay que tener cuidado al elegir el lugar donde definimos la plantilla de estructura. Lo más normal es declararla globalmente para que pueda ser utilizada en cualquier lugar del programa. A la hora de definir la plantilla de estructura podemos omitir, o bien, el parámetro nombre_estructura, o bien, el parámetro lista_variables_estructura. En el caso de que se omita el parámetro nombre_estructura, sólo podremos usar la plantilla para definir las variables que aparezcan en el parámetro lista_variables_estructura. Si posteriormente queremos declarar alguna otra variable utilizando esta plantilla, tendremos que volver a definirla. Si el parámetro que omitimos es lista_variables_estructura, lo que habremos hecho es crear una plantilla de estructura sin variables asociadas, las cuales podremos crear en cualquier momento, usando el modo de declaración de estructura visto anteriormente, es decir: struct nombre_estructura nombre_variable_nueva; En ningún caso podremos omitir, a la vez, el nombre_estructura y la lista_variables_estructura. Veamos ahora como podemos acceder a los elementos individuales que forman las estructuras. Para dichos accesos se utiliza el operador ".". Veamos un ejemplo de acceso a una estructura. Supongamos que tenemos la siguiente estructura: struct coordenada { int x; int y; int z; } a, b, c; Para llenar la variable estructura "a" bastaría con hacer: a.x = 5; a.y = 23; a.z = 10; En cualquier momento podremos leer estos datos: int eje_x, eje_y, eje_z; eje_x = a.x; eje_y = a.y; eje_z = a.z; Otra operación que podremos realizar son asignaciones entre variables estructuras con la misma plantilla: c = a; b = c; Los tipos de los elementos individuales de una estructura pueden ser simples (int, float, char, …) o complejos (array, otra estructura, …). Veamos un ejemplo de una plantilla de estructura que combina elementos individuales simples y complejos. struct fecha { int dia; int mes; int anyo; }; struct hora { int horas; int minutos; }; struct ficha { char nombre[20]; char apellidos[40]; struct fecha dia_nacimiento; struct hora hora_nacimiento; }; Si queremos definir una variable tipo ficha, y queremos poner la fecha de nacimiento haremos: struct ficha persona; persona.fecha.dia = 20; persona.fecha.mes = 2; persona.fecha.anyo = 2001; Otra posibilidad frecuentemente utilizada que se le ofrece al programador es la de definir tablas de estructuras. De esta manera se permite gestionar una gran cantidad de información fácilmente. Un ejemplo sencillo de esto sería por ejemplo una matriz de coordenadas. Teniendo en cuenta la estructura coordenada definida más arriba podríamos definir la siguiente matriz: struct coordenada matriz1[10][10]; En este caso una posible inicialización de la anterior matriz en el que todos sus componentes toman como valor el origen de coordenadas quedaría de la siguiente manera: for (cont_filas=0; cont<10; cont_filas++) { for (cont_columnas=0; cont_columnas<10; cont_columnas++) { matriz[cont_filas][cont_columnas].x = 0; matriz[cont_filas][cont_columnas].y = 0; matriz[cont_filas][cont_columnas].z = 0; } } Ejemplos más complejos se podrían crear a partir de la estructura ficha de forma que se pudieran gestionar los datos de toda una población de individuos. En este caso tendríamos por ejemplo: struct ficha poblacion[100]; // Vector que sirve para gestionar los datos personales de 100 individuos. Al igual que con el resto de tipos de variables, C permite crear variables punteros a estructuras. La forma de declarar estas variables es: struct nombre_estructura *nombre_varible_estructura; Para poder acceder a los elementos individuales de una variable puntero a estructura se utiliza el operador -> (el operador, denominado comunmente flecha, está formado por un signo menos seguido de un signo de mayor, sin ningún espacio en blanco entre ellos). Siguiendo con el ejemplo anterior de las coordenadas, vamos a definir un puntero a una variable de estructura de la siguiente forma: struct coordenada a, b, *p, *q; a.x = 5; a.y = 23; a.z = 10; b = a; p = &b; /* &b nos devuelve la dirección de la estructura b */ p->x = 2; p->y = a.y; p->z = a.z; q = p; /* p y q apuntan a la misma estructura */ Al igual que ocurría en el apartado anterior también existe la posibilidad de declarar tablas de estructuras. El formato sería el siguiente: struct nombre_estructura *nombre_varible_estructura[N]; o struct nombre_estructura *nombre_varible_estructura[N][M]; Por supuesto también se pueden declarar tablas de punteros a estructuras de más de dos dimensiones pero al igual que ocurría con la declaración de tablas de tipos de datos básicos, no suelen ser muy usadas. Un ejemplo de declaración de un vector de punteros a estructuras y un acceso al campo de una estructura sería la siguiente: struct coordenada coor1, *vector_punteros_coordenadas[10]; vector_punteros_coordenadas[4] = &coor1; vector_punteros_coordenadas[4]->y = 2; Al igual que ocurría con los punteros a tipos básicos de datos también se pueden pasar punteros a estructuras en las llamadas a funciones. Así por ejemplo tendríamos: struct coordenada coordenada1, *p_coordenada1; flota distancia; coordenada1.x = 5; coordenada1.y = -11; p_coordenada1 = &coordenada1; cambiar_a_punto_simétrico_en_cuadrante (p_coordenada1); ó cambiar_a_punto_simetrico_en_cuadrante (&coordenada1); ... ... ... /* La siguiente función intercambia la coordenada x por la y */ void cambiar_a_punto_simétrico_en_cuadrante(struct coordenada *c) { int aux; aux = (*c).x; (*c).x = (*c).y; (*c).y = aux; } Nótese la equivalencia entre la notación c->x y (*c).x En el lenguaje C, cuando hablamos de unión nos estamos refiriendo a una porción de memoria compartida por varias variables, las cuales pueden ser de distintos tipos. Para declarar una unión usamos la palabra clave union de la siguiente forma: union nombre_union { tipo variable1; tipo variable2; ... ... tipo variableN; } lista_variables_union; Como se ve, la forma de declaración de declaración es similar a la de una estructura, de forma que podremos declarar una variable, o bien colocando su nombre al final de la declaración inicial, o posteriormente, utilizando una declaración aparte como muestra el siguiente código: union nombre_union nombre_variable_nueva; La forma de funcionamiento de las uniones es muy simple. Cuando definimos una unión y declaramos variables asociadas, el compilador reservará para cada variable, un espacio de memoria cuyo tamaño coincidirá con el tamaño del elemento más grande que se defina dentro de la unión. Supongamos que tenemos la siguiente declaración: union prueba_union { char letra; char cadena[8]; int numero; } u1; El compilador reservará, para la variable u1, 8 bytes de memoria (que corresponde con el tamaño del elemento cadena), de forma que en caso de acceder al elemento letra, estaremos accediendo al primer byte de la variable, mientras que si accedemos al elemento numero, estaremos accediendo a los dos primeros bytes de la variable. Por último, si accedemos al elemento cadena estaremos accediendo a todos los bytes de la variable. Una enumeración es un conjunto de constantes enteras con nombre, que especifica todos los valores válidos que una variable de este tipo puede tener. La forma de definir una enumeración es: enum nombre_enum{lista_de_enum} variables_de_enum; Veamos un ejemplo: enum color{blanco, color_mesa; amarillo, rojo, verde, azul, marron, negro} Como con las estructuras, C permite definir posteriormente más variables enumeradas del tipo color, la forma de hacerlo sería la siguiente: enum color color_coche; Una vez definidas las variables enumeradas, las siguientes operaciones serían válidas: color_mesa = verde; color_coche = color_mesa; if(color_mesa == azul) printf("La mesa es de color azul\n"); switch(color_coche) { case blanco: printf("El coche es de color blanco\n"); break; case amarillo: printf("El coche es de color amarillo\n"); break; case rojo: printf("El coche es de color rojo\n"); break; case verde: printf("El coche es de color verde\n"); break; case azul: printf("El coche es de color azul\n"); break; case marron: printf("El coche es de color marron\n"); break; case negro: printf("El coche es de color negro\n"); break; } Para poder definir nuevos nombres de tipos de datos, usamos la palabra clave typedef. Con esto, realmente no estamos creando un nuevo tipo de datos, sino que se define un nuevo nombre a un tipo ya existente. También podemos usar typedef para asignar un nombre de tipo a una estructura o a una enumeración, de forma que para declarar las variables de estos tipos no necesitaremos precederlos de las palabras clave struct o enum, respectivamente. La forma general de la sentencia typedef es: typedef tipo nombre_nuevo_tipo; Algunos ejemplos podrían ser: typedef int ENTERO; typedef unsigned char BYTE; typedef struct fecha FECHA; De forma que podremos definir variables como: ENTERO *e1, e2; BYTE *b1, b2; FECHA *f1, f2, lista_fechas[20]; Un uso típico al definir estructuras es: typedef struct { float x,y,z; } coordenada; Si queremos definir una variable de este tipo de estructura basta con poner: coordenada a; Y a será una variable de estructura coordenada, lo que clarifica el código.