TIPOS DE DATOS ESTRUCTURADOS INHOMOGENEOS (struct).







Introducción.
La sección anterior trataba la forma de crear listas (tablas, matrices) de tipos de datos atómicos. Cuando el tipo base de la lista creada pasa a ser estructurado, siguen en vigor los mismos principios: las listas se almacenan en posiciones secuenciales de memoria, salvo campos de relleno, y las tablas se almacenan por filas. Ciertamente, el acceso a campos de tipos estructurados conlleva ciertas diferencias de manejo respecto al caso de listas de tipos atómicos. Estas diferencias, y algunas otras circunstancias de interés, se estudian a continuación.

Listas, tablas y matrices de tipos estructurados:Declaración e Iniciación
La forma correcta de declarar una variable matricial cuyo tipo base no sea atómico pasa por la definición previa de ese nuevo tipo base. Consiguientemente, se tienen los pasos siguientes:

/* Declaración de un nuevo tipo base */
struct Tipo_base {
  Tipo_1 campo_1;
  Tipo_2 campo_2;
  Tipo_N campo_m; } ;
/* Declaración de una variable matricial de este tipo base */
struct Tipo_base variable_matricial[DIM_1][DIM_2]...[DIM_P];



El mecanismo de iniciación es análogo al empleado en casos anteriores, si bien será preciso respetar los tipos. El número y tipo de los valores indicados entre llaves tienen que coincidir con el número y tipo de los campos existentes en los elementos de la variable matricial. En la representación gráfica se aprecia, en la parte superior, la definición de un tipo estructurado: los campos coloreados se han mostrado con dimensiones proporcionales a su extensión medida en bytes. Por su parte, la estructura matricial declarada en la parte inferior consta de filas y columnas, que se han mostrado en una distribución rectangular. Como es sabido, las matrices se almacenarán por filas: ese es el orden que deben seguir los campos empleados para dar valores iniciales. Si se efectuara una declaración con iniciación, el resultado debería ser similar al que se muestra en el gráfico siguiente. Obsérvese que se respeta en todo momento el tipo y posición de los campos iniciados.

Acceso y Asignación
La sintaxis de acceso a un elemento completo (una estructura) de una variable matricial de tipo base inhomogéneo es la habitual: basta escribir el nombre de la variable matricial, seguida por los oportunos índices entre corchetes. Se obtiene así el nombre de una estructura. Si se desea acceder a un campo concreto, basta añadir un punto y el nombre del campo. Las asignaciones de campos atómicos se efectúan del modo habitual (operador =). Las asignaciones de campos matriciales se efectúan mediante la oportuna llamada a las funciones strcpy() o memcpy(). Véase un ejemplo.

Grupos de Cadenas
Un libro es una matriz tridimensional de capas (hojas) por filas (líneas) por columnas. Esto es un ejemplo de matriz tridimensional de caracteres; el lenguaje C trata las matrices de caracteres de forma relativamente especial, por cuanto precisan de un carácter terminador. En todo caso, la significación de las diferentes posibilidades de uso de índices sigue la estructura habitual, esto es, un número de índices menor que el de dimensiones define un objeto de tantas dimensiones como las totales menos el número de índices empleados. Si la construcción de la matriz se lleva a cabo siguiendo las reglas correctas de iniciación, el uso de estas expresiones producirá siempre una línea: esto se debe a que el compilador irá escribiendo caracteres sucesivos hasta encontrar el oportuno marcador. Si este no existiera, los resultados no están definidos. Véase el oportuno Ejemplo.

Ejemplo 1.- Construir un programa que muestre una 3x3 formada por elementos de tipo struct. Realizar y verificar la oportuna iniciación. Realizar y verificar una asignación parcial a otra matriz de idénticas dimensiones.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define DIM_1 3
#define DIM_2 3
#define DIM_3 3

/* Definición del tipo base */
struct Tipo_base {
  int campo_1;
  float campo_2;
  double campo_3;
  char campo_4[10]; };

/* Declaración de la variable estructurada */
struct Tipo_base variable_matricial[DIM_1][DIM_2][DIM_3]
  = {  1,  2.0,  3.0, "Fila uno", 4,  5.0,  6.0, "Fila uno", 7,  8.0,  9.0,
  "Fila uno",10, 11.0, 12.0, "Fila dos",13, 14.0, 15.0, "Fila dos",16, 17.0, 18.0,
  "Fila dos",19, 20.0, 21.0, "Fila tres",22, 23.0, 24.0, "Fila tres",25, 26.0, 27.0,
  "Fila tres"},
    v2[DIM_1][DIM_2][DIM_3];
/* Obsérvese que se hace una iniciación parcial. Concretamente, solo
se inicia la primera capa u hoja de estructuras; las dos siguientes no
se inician.
*/
int capa, fila, columna; /* Indices para estudiar el contenido de la matriz */

int main(void)
{
  printf("Variables Matriciales con tipo base inhomogéneo.\n\n");
/* Se muestran los tamaños */
  printf("struct Tipo_base {int campo_1;\
    float campo_2;double campo_3; }; Tamaño = %d\n\n",
    sizeof(struct Tipo_base));
  printf("struct Tipo_base variable_matricial[%d][%d][%d]; Tamaño = %d\n\n",
    DIM_1, DIM_2, DIM_3, sizeof(variable_matricial));
/* Se muestra el contenido de la matriz */
  printf("\n\nContenido de la variable matricial:\n\n");
  for(capa=0;capa<DIM_1;capa++)
    for(fila=0;fila<DIM_2;fila++)for(columna=0;columna<DIM_3;columna++)
  printf("m[%d][%d][%d] = %4d, %6.2f\  , %6.2f, %10s.\n",capa, fila, columna,
  variable_matricial[capa][fila][columna].campo_1,
  variable_matricial[capa][fila][columna].campo_2,
  variable_matricial[capa][fila][columna].campo_3,
  variable_matricial[capa][fila][columna].campo_4);

  /* Ahora se hace una copia parcial, de solo tres capas. ¿Cuantos
    elementos forman una capa? El producto de filas por columnas */

  memcpy(v2, variable_matricial, DIM_2*DIM_3*sizeof(struct Tipo_base));

  /* Y se muestra solo la primera capa de v2 */
  printf("\nResultado tras la asignación.\n\n");
  capa=0;
  for(fila=0;fila<DIM_2;fila++)
    for(columna=0;columna<DIM_3;columna++)
    printf("v2[%d][%d][%d] = %4d, %6.2f,\%6.2f, %10s.\n",capa, fila, columna,
  v2[capa][fila][columna].campo_1,v2[capa][fila][columna].campo_2,
  v2[capa][fila][columna].campo_3, v2[capa][fila][columna].campo_4);

  printf("\nTodo coincide. Q.E.D.\n\nTerminación normal del programa.\n\n");
  return 0;
}

/*
  RESULTADO
Variables Matriciales con tipo base inhomogéneo.

struct Tipo_base {int campo_1;float campo_2;double campo_3; }; Tamaño = 28

struct Tipo_base variable_matricial[3][3][3]; Tamaño = 756



Contenido de la variable matricial:

m[0][0][0] =    1,   2.00,   3.00,   Fila uno.
m[0][0][1] =    4,   5.00,   6.00,   Fila uno.
m[0][0][2] =    7,   8.00,   9.00,   Fila uno.
m[0][1][0] =   10,  11.00,  12.00,   Fila dos.
m[0][1][1] =   13,  14.00,  15.00,   Fila dos.
m[0][1][2] =   16,  17.00,  18.00,   Fila dos.
m[0][2][0] =   19,  20.00,  21.00,  Fila tres.
m[0][2][1] =   22,  23.00,  24.00,  Fila tres.
m[0][2][2] =   25,  26.00,  27.00,  Fila tres.
m[1][0][0] =    0,   0.00,   0.00,           .
m[1][0][1] =    0,   0.00,   0.00,           .
m[1][0][2] =    0,   0.00,   0.00,           .
m[1][1][0] =    0,   0.00,   0.00,           .
m[1][1][1] =    0,   0.00,   0.00,           .
m[1][1][2] =    0,   0.00,   0.00,           .
m[1][2][0] =    0,   0.00,   0.00,           .
m[1][2][1] =    0,   0.00,   0.00,           .
m[1][2][2] =    0,   0.00,   0.00,           .
m[2][0][0] =    0,   0.00,   0.00,           .
m[2][0][1] =    0,   0.00,   0.00,           .
m[2][0][2] =    0,   0.00,   0.00,           .
m[2][1][0] =    0,   0.00,   0.00,           .
m[2][1][1] =    0,   0.00,   0.00,           .
m[2][1][2] =    0,   0.00,   0.00,           .
m[2][2][0] =    0,   0.00,   0.00,           .
m[2][2][1] =    0,   0.00,   0.00,           .
m[2][2][2] =    0,   0.00,   0.00,           .

Resultado tras la asignación.

v2[0][0][0] =    1,   2.00,   3.00,   Fila uno.
v2[0][0][1] =    4,   5.00,   6.00,   Fila uno.
v2[0][0][2] =    7,   8.00,   9.00,   Fila uno.
v2[0][1][0] =   10,  11.00,  12.00,   Fila dos.
v2[0][1][1] =   13,  14.00,  15.00,   Fila dos.
v2[0][1][2] =   16,  17.00,  18.00,   Fila dos.
v2[0][2][0] =   19,  20.00,  21.00,  Fila tres.
v2[0][2][1] =   22,  23.00,  24.00,  Fila tres.
v2[0][2][2] =   25,  26.00,  27.00,  Fila tres.

Todo coincide. Q.E.D.

Terminación normal del programa.

*/
/*
  COMENTARIOS

  Obsérvese la especificación de número total de filas (6)
  y decimales (2) en las variables reales. El ajuste se
  hace a la izquierda.
  Obsérvese la especificación del número total de filas (10)
  en el campo alfanumérico. El ajuste es a la DERECHA.
  Obsérvese que solo se ha copiado una capa, la primera. El
  tiempo se reduce al 33% del necesario para duplicar
  toda la matriz. Esto solo puede hacerse, obviamente, cuando
  se conoce exactamente el número de elementos que hay que
  copiar.
*/


Volver a la parte superior de esta página

Ejemplo 2.- Construir una estructura matricial que simule el comportamiento de un libro. El programa mostrará inicialmente la primera página, y permitirá al usuario avanzar, retroceder o salir.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>

#define PAGINAS 3
#define LINEAS 10
#define COLUMNAS 20

char libro[PAGINAS][LINEAS][COLUMNAS] =
  {
    "Página 1, línea 1",
    "Página 1, línea 2",
    "Página 1, línea 3",
    "Página 1, línea 4",
    "Página 1, línea 5",
    "Página 1, línea 6",
    "Página 1, línea 7",
    "Página 1, línea 8",
    "Página 1, línea 9",
    "Página 1, línea 10",

    "Página 2, línea 1",
    "Página 2, línea 2",
    "Página 2, línea 3",
    "Página 2, línea 4",
    "Página 2, línea 5",
    "Página 2, línea 6",
    "Página 2, línea 7",
    "Página 2, línea 8",
    "Página 2, línea 9",
    "Página 2, línea 10",

    "Página 3, línea 1",
    "Página 3, línea 2",
    "Página 3, línea 3",
    "Página 3, línea 4",
    "Página 3, línea 5",
    "Página 3, línea 6",
    "Página 3, línea 7",
    "Página 3, línea 8",
    "Página 3, línea 9",
    "Página 3, línea 10"};

int fin;
char instruccion;
char menu[80];
char opcionesvalidas[80];

/*
 DECLARACIONES ESPECIFICAS
*/
int num_pagina = 0, linea;
void main(void)
{
 /* Iniciaciones generales */

 fin = 0;
 strcpy(menu,"A)vanzar R)etroceder S)alir");
 strcpy(opcionesvalidas,"ARS");
 for(linea=0;linea<LINEAS;linea++)
  printf("%s\n",libro[num_pagina][linea]);
 do {

  do {
   puts(menu);scanf("%c%*c",&instruccion);
   instruccion=toupper(instruccion);
  } while (!strchr(opcionesvalidas,instruccion));

  switch(instruccion)
  {
   case 'A':
    {
     (num_pagina < PAGINAS - 1)? num_pagina++ : (num_pagina = 0);
     for(linea=0;linea<LINEAS;linea++)
     printf("%s\n",libro[num_pagina][linea]);
    }
    break;
   case 'R':
    {
     (num_pagina > 0)? (num_pagina--) : (num_pagina = PAGINAS-1);
     for(linea=0;linea<LINEAS;linea++)
      printf("%s\n",libro[num_pagina][linea]);
    }
    break;
   case 'S':
    {
     fin = 1;
     puts("Nos vamos!\n");
    }
    break;
   default :
    {
     puts("Instrucción incorrecta.\n");
    }
    break;
  }

 }
 while (!fin);

 puts("\n\nThat's all folks!\n");
}
/*
 COMENTARIOS

 Obsérvese el uso de una estrategia circular.
 Obsérvese el uso de una expresión con dos índices
 en el contexto de una matriz de 3 dimensiones. El
 primer índice denota una capa u hoja, y el segundo
 una fila de esa capa.

*/


Volver a la parte superior de esta página

Ejercicios propuestos



  1. Ejercicio 0602r01.-Sea un registro definido mediante la estructura siguiente:
    struct Registro {
      char nombre[20];
      char apellidos[20];
      int edad;
      float peso;
      long clave;
    };
    Se pide construir un módulo dotado de las funciones necesarias para tratar una lista formada por registros del tipo anterior. Concretamente, el módulo debe ofrecer las funciones necesarias para: Este ejercicio debe realizarse con variables estáticas.

  2. Ejercicio 0602r02.-Rehacer el módulo del ejercicio 1 empleando una estructura lineal dinámica (una lista dinámica de punteros de registros) Considerar la posibilidad de que se agote el espacio reservado (en la lista de punteros, realloc()).

  3. Ejercicio 0602r03.-Rehacer el módulo del ejercicio 1 empleando un archivo binario. Observar que en este caso la limitación de espacio está dada por el espacio disponible en disco. ¿Es sustancial la diferencia de prestaciones?

  4. Ejercicio 0602r04.-Construir una base de datos dotada de un generador de informes basado en una pila estática formada por índices de registros.

  5. Ejercicio 0602r05.-Construir una base de datos con la capacidad de mostrar informes ordenados por cualquier campo. La ordenación se realizará mediante una pila.

  6. Ejercicio 0602r06.-Una base de datos formada por registros similares a struct Registro está almacenada en una lista simplemente enlazada. Construir un módulo adecuado para Volcar todos los nodos a disco, sin punteros. Leer todos los nodos de disco, reconstruyendo la lista.

  7. Ejercicio 0602r07.-Rehacer el módulo anterior empleando una lista doblemente enlazada.

  8. Ejercicio 0602r08.-Construir un módulo adecuado para construir una lista enlazada circular de nodos.