Curso de CEl Rincón del C - www.elrincondelc.com

Estructuras

[Anterior] [Siguiente] [Contenido]

Contenido

Estructuras

Supongamos que queremos hacer una agenda con los números de teléfono de nuestros amigos. Necesitaríamos un array de Cadenas para almacenar sus nombres, otro para sus apellidos y otro para sus números de teléfono. Esto puede hacer que el programa quede desordenado y difícil de seguir. Y aquí es donde vienen en nuestro auxilio las estructuras.

Para definir una estructura usamos el siguiente formato:

struct nombre_de_la_estructura {
       campos de estructura;
       };

NOTA: Es importante no olvidar el ';' del final, si no a veces se obtienen errores extraños.

Para nuestro ejemplo podemos crear una estructura en la que almacenaremos los datos de cada persona. Vamos a crear una declaración de estructura llamada amigo:

struct estructura_amigo {
       char nombre[30];
       char apellido[40];
       char telefono[10];
       char edad;
       };

A cada elemento de esta estructura (nombre, apellido, teléfono) se le llama campo o miembro. (NOTA: He declarado edad como char porque no conozco a nadie con más de 127 años.

Ahora ya tenemos definida la estructura, pero aun no podemos usarla. Necesitamos declarar una variable con esa estructura.

struct estructura_amigo amigo;

Ahora la variable amigo es de tipo estructura_amigo. Para acceder al nombre de amigo usamos: amigo.nombre. Vamos a ver un ejemplo de aplicación de esta estructura. (NOTA: En el siguiente ejemplo los datos no se guardan en disco así que cuanda acaba la ejecución del programa se pierden).

#include <stdio.h>

struct estructura_amigo {	/* Definimos la estructura estructura_amigo */
       char nombre[30];
       char apellido[40];
       char telefono[10];
       char edad;
       };

struct estructura_amigo amigo;

int main()
     {
     printf( "Escribe el nombre del amigo: " );
     fflush( stdout );
     scanf( "%s", &amigo.nombre );
     printf( "Escribe el apellido del amigo: " );
     fflush( stdout );
     scanf( "%s", &amigo.apellido );
     printf( "Escribe el número de teléfono del amigo: " );
     fflush( stdout );
     scanf( "%s", &amigo.telefono );
     printf( "El amigo %s %s tiene el número: %s.\n", amigo.nombre,
             amigo.apellido, amigo.telefono );
     }
Comprobado con DJGPP

Este ejemplo estaría mejor usando gets que scanf, ya que puede haber nombres compuestos que scanf no cogería por los espacios.

Se podría haber declarado directamente la variable amigo:

struct estructura_amigo {
       char nombre[30];
       char apellido[40];
       char telefono[10];
       } amigo;

[Arriba]

Arrays de estructuras

Supongamos ahora que queremos guardar la información de varios amigos. Con una variable de estructura sólo podemos guardar los datos de uno. Para manejar los datos de más gente (al conjunto de todos los datos de cada persona se les llama REGISTRO) necesitamos declarar arrays de estructuras.

¿Cómo se hace esto? Siguiendo nuestro ejemplo vamos a crear un array de ELEMENTOS elementos:

struct estructura_amigo amigo[ELEMENTOS];

Ahora necesitamos saber cómo acceder a cada elemento del array. La variable definida es amigo, por lo tanto para acceder al primer elemento usaremos amigo[0] y a su miembro nombre: amigo[0].nombre. Veamoslo en un ejemplo en el que se supone que tenemos que meter los datos de tres amigos:

#include <stdio.h>

#define ELEMENTOS       3

struct estructura_amigo {
       char nombre[30];
       char apellido[40];
       char telefono[10];
       int edad;
       };

struct estructura_amigo amigo[ELEMENTOS];

int main()
     {
     int num_amigo;

     for( num_amigo=0; num_amigo<ELEMENTOS; num_amigo++ )
          {
          printf( "\nDatos del amigo número %i:\n", num_amigo+1 );
          printf( "Nombre: " ); fflush( stdout );
          gets(amigo[num_amigo].nombre);
          printf( "Apellido: " ); fflush( stdout );
          gets(amigo[num_amigo].apellido);
          printf( "Teléfono: " ); fflush( stdout );
          gets(amigo[num_amigo].telefono);
          printf( "Edad: " ); fflush( stdout );
          scanf( "%i", &amigo[num_amigo].edad );
          while(getchar()!='\n');
          }
     /* Ahora imprimimos sus datos */
     for( num_amigo=0; num_amigo<ELEMENTOS; num_amigo++ )
          {
          printf( "El amigo %s ", amigo[num_amigo].nombre );
          printf( "%s tiene ", amigo[num_amigo].apellido );
          printf( "%i años ", amigo[num_amigo].edad );
          printf( "y su teléfono es el %s.\n" , amigo[num_amigo].telefono );
          }
     }
Comprobado con DJGPP

IMPORTANTE: Quizás alguien se pregunte qué pinta la línea esa de while(getchar()!='\n');. Esta línea se usa para vaciar el buffer de entrada. Para más información consulta Qué son los buffer y cómo funcionan.

[Arriba]

Inicializar una estructura

A las estructuras se les pueden dar valores iniciales de manera análoga a como hacíamos con los arrays. Primero tenemos que definir la estructura y luego cuando declaramos una variable como estructura le damos el valor inicial que queramos. Recordemos que esto no es en absoluto necesario. Para la estructura que hemos definido antes sería por ejemplo:

struct estructura_amigo amigo = {
       "Juanjo",
       "Lopez",
       "592-0483",
       30
       };

NOTA: En algunos compiladores es posible que se exiga poner antes de struct la palabra static.

Por supuesto hemos de meter en cada campo el tipo de datos correcto. La definición de la estructura es:

struct estructura_amigo {
        char nombre[30];
        char apellido[40];
        char telefono[10];
        int edad;
        };

por lo tanto el nombre ("Juanjo") debe ser una cadena de no más de 29 letras (recordemos que hay que reservar un espacio para el símbolo '\0'), el apellido ("Lopez") una cadena de menos de 39, el teléfono una de 9 y la edad debe ser de tipo char.

Vamos a ver la inicialización de estructuras en acción:

#include <stdio.h>

struct estructura_amigo {
        char nombre[30];
        char apellido[40];
        char telefono[10];
        int edad;
        };

struct estructura_amigo amigo = {
       "Juanjo",
       "Lopez",
       "592-0483",
       30
       };

int main()
      {
      printf( "%s tiene ", amigo.apellido );
      printf( "%i años ", amigo.edad );
      printf( "y su teléfono es el %s.\n" , amigo.telefono );
      }
Comprobado con DJGPP

También se puede inicializar un array de estructuras de la forma siguiente:

struct estructura_amigo amigo[] =
       {
       "Juanjo", "Lopez", "504-4342", 30,
       "Marcos", "Gamindez", "405-4823", 42,
       "Ana", "Martinez", "533-5694", 20
       };

En este ejemplo cada línea es un registro. Como sucedía en los arrays si damos valores iniciales al array de estructuras no hace falta indicar cuántos elementos va a tener. En este caso la matriz tiene 3 elementos, que son los que le hemos pasado.

[Arriba]

Punteros a estructuras

Cómo no, también se pueden usar punteros con estructuras. Vamos a ver como funciona esto de los punteros con estructuras. Primero de todo hay que definir la estructura de igual forma que hacíamos antes. La diferencia está en que al declara la variable de tipo estructura debemos ponerle el operador '*' para indicarle que es un puntero.

Creo que es importante recordar que un puntero no debe apuntar a un lugar cualquiera, debemos darle una dirección válida donde apuntar. No podemos por ejemplo crear un puntero a estructura y meter los datos directamente mediante ese puntero, no sabemos dónde apunta el puntero y los datos se almacenarían en un lugar cualquiera.

Y para comprender cómo funcionan nada mejor que un ejemplo. Este programa utiliza un puntero para acceder a la información de la estructura:

#include <stdio.h>

struct estructura_amigo {
        char nombre[30];
        char apellido[40];
        char telefono[10];
        int edad;
        };

struct estructura_amigo amigo = {
       "Juanjo",
       "Lopez",
       "592-0483",
       30
       };

struct estructura_amigo *p_amigo;

int main()
      {
      p_amigo = &amigo;
      printf( "%s tiene ", p_amigo->apellido );
      printf( "%i años ", p_amigo->edad );
      printf( "y su teléfono es el %s.\n" , p_amigo->telefono );
      }
Comprobado con DJGPP

Has la definición del puntero p_amigo vemos que todo era igual que antes. p_amigo es un puntero a la estructura estructura_amigo. Dado que es un puntero tenemos que indicarle dónde debe apuntar, en este caso vamos a hacer que apunte a la variable amigo:

      p_amigo = &amigo;

No debemos olvidar el operador & que significa 'dame la dirección donde está almacenado...'.

Ahora queremos acceder a cada campo de la estructura. Antes lo hacíamos usando el operador '.', pero, como muestra el ejemplo, si se trabaja con punteros se debe usar el operador '->'. Este operador viene a significar algo así como: "dame acceso al miembro ... del puntero ...".

Ya sólo nos queda saber cómo podemos utilizar los punteros para introducir datos en las estructuras. Lo vamos a ver un ejemplo:

#include <stdio.h>

struct estructura_amigo {
       char nombre[30];
       char apellido[40];
       int edad;
       };

struct estructura_amigo amigo, *p_amigo;

int main()
     {
     p_amigo = &amigo;

     /* Introducimos los datos mediante punteros */
     printf("Nombre: ");fflush(stdout);
     gets(p_amigo->nombre);
     printf("Apellido: ");fflush(stdout);
     gets(p_amigo->apellido);
     printf("Edad: ");fflush(stdout);
     scanf( "%i", &p_amigo->edad );

      /* Mostramos los datos */
      printf( "El amigo %s ", p_amigo->nombre );
      printf( "%s tiene ", p_amigo->apellido );
      printf( "%i años.\n", p_amigo->edad );
      }
Comprobado con DJGPP

NOTA: p_amigo es un puntero que apunta a la estructura amigo. Sin embargo p_amigo->edad es una variable de tipo int. Por eso al usar el scanf tenemos que poner el &.

[Arriba]

Punteros a arrays de estructuras

Por supuesto también podemos usar punteros con arrays de estructuras. La forma de trabajar es la misma, lo único que tenemos que hacer es asegurarnos que el puntero inicialmente apunte al primer elemento, luego saltar al siguiente hasta llegar al último.

#include <stdio.h>

#define ELEMENTOS       3

struct estructura_amigo {
       char nombre[30];
       char apellido[40];
       char telefono[10];
       int edad;
       };

struct estructura_amigo amigo[] =
       {
       "Juanjo", "Lopez", "504-4342", 30,
       "Marcos", "Gamindez", "405-4823", 42,
       "Ana", "Martinez", "533-5694", 20
       };

struct estructura_amigo *p_amigo;

int main()
     {
     int num_amigo;
     p_amigo = amigo;  /* apuntamos al primer elemento del array */

     /* Ahora imprimimos sus datos */
     for( num_amigo=0; num_amigo<ELEMENTOS; num_amigo++ )
          {
          printf( "El amigo %s ", p_amigo->nombre );
          printf( "%s tiene ", p_amigo->apellido );
          printf( "%i años ", p_amigo->edad );
          printf( "y su teléfono es el %s.\n" , p_amigo->telefono );
          /* y ahora saltamos al siguiente elemento */
          p_amigo++;
          }
     }
Comprobado con DJGPP

En vez de p_amigo = amigo; se podía usar la forma p_amigo = &amigo[0];, es decir que apunte al primer elemento (el elemento 0) del array. La primera forma creo que es más usada pero la segunda quizás indica más claramente al lector principiante lo que se pretende.

Ahora veamos el ejemplo anterior de cómo introducir datos en un array de estructuras mediante punteros:

#include <stdio.h>

#define ELEMENTOS       3

struct estructura_amigo {
       char nombre[30];
       char apellido[40];
       int edad;
       };

struct estructura_amigo amigo[ELEMENTOS], *p_amigo;

int main()
     {
     int num_amigo;
     /* apuntamos al primer elemento */
     p_amigo = amigo;

     /* Introducimos los datos mediante punteros */
     for ( num_amigo=0; num_amigo<ELEMENTOS; num_amigo++ )
         {
         printf("Datos amigo %i\n",num_amigo);
         printf("Nombre: ");fflush(stdout);
         gets(p_amigo->nombre);
         printf("Apellido: ");fflush(stdout);
         gets(p_amigo->apellido);
         printf("Edad: ");fflush(stdout);
         scanf( "%i", &p_amigo->edad );
         /* vaciamos el buffer de entrada */
         while(getchar()!='\n');
         /* saltamos al siguiente elemento */
         p_amigo++;
         }
     /* Ahora imprimimos sus datos */
     p_amigo = amigo;
     for( num_amigo=0; num_amigo<ELEMENTOS; num_amigo++ )
          {
          printf( "El amigo %s ", p_amigo->nombre );
          printf( "%s tiene ", p_amigo->apellido );
          printf( "%i años.\n", p_amigo->edad );
          p_amigo++;
          }
     }
Comprobado con DJGPP

Es importante no olvidar que al terminar el primer bucle for el puntero p_amigo apunta al último elemento del array de estructuras. Para mostrar los datos tenemos que hacer que vuelva a apuntar al primer elemento y por eso usamos de nuevo p_amigo=amigo; (en negrita).

[Arriba]

Paso de estructuras a funciones

Las estructuras se pueden pasar directamente a una función igual que hacíamos con las variables. Por supuesto en la definición de la función debemos indicar el tipo de argumento que usamos:

int nombre_función ( struct nombre_de_la_estructura nombre_de_la variable_estructura )

En el ejemplo siguiente se usa una función llamada suma que calcula cual será la edad 20 años más tarde (simplemente suma 20 a la edad). Esta función toma como argumento la variable estructura arg_amigo. Cuando se ejecuta el programa llamamos a suma desde main y en esta variable se copia el contenido de la variable amigo.

Esta función devuelve un valor entero (porque está declarada como int) y el valor que devuelve (mediante return) es la suma.

#include <stdio.h>

struct estructura_amigo {
        char nombre[30];
        char apellido[40];
        char telefono[10];
        int edad;
        };

int suma( struct estructura_amigo arg_amigo )
    {
    return arg_amigo.edad+20;
    }
int main()
      {
      struct estructura_amigo amigo = {
             "Juanjo",
             "Lopez",
             "592-0483",
             30
       };
      printf( "%s tiene ", amigo.apellido );
      printf( "%i años ", amigo.edad );
      printf( "y dentro de 20 años tendra %i.\n", suma(amigo) );
      system( "PAUSE" );
      }

Fichero: estructuras_funciones.c

Si dentro de la función suma hubiésemos cambiado algún valor de la estructura, dado que es una copia no hubiera afectado a la variable amigo de main. Es decir, si dentro de 'suma' hacemos arg_amigo.edad = 20; el valor de arg_amigo cambiará, pero el de amigo seguirá siendo 30.

También se pueden pasar estructuras mediante punteros o se puede pasar simplemente un miembro (o campo) de la estructura.

Si usamos punteros para pasar estructuras como argumentos habrá que hacer unos cambios al código anterior (en negrita y subrrayado):

#include <stdio.h>

...

int suma( struct estructura_amigo *arg_amigo )
    {
    return arg_amigo->edad+20;
    }
int main()
      {
      ...
      printf( "%s tiene ", amigo.apellido );
      printf( "%i anos ", amigo.edad );
      printf( "y dentro de 20 anos tendra %i.\n", suma(&amigo) );
      system( "PAUSE" );
      }

>
Fichero: estructuras_funciones2.c

Lo primero será indicar a la función suma que lo que va a recibir es un puntero, para eso ponemos el * (asterisco). Segundo, como dentro de la función suma usamos un puntero a estructura y no una variable estructura debemos cambiar el '.' (punto) por el '->'. Tercero, dentro de main cuando llamamos a suma debemos pasar la dirección de amigo, no su valor, por lo tanto debemos poner '&' delante de amigo.

Si usamos punteros a estructuras corremos el riesgo (o tenemos la ventaja, según cómo se mire) de poder cambiar los datos de la estructura de la variable amigo de main.

Pasar sólo miembros de la estructura

Otra posibilidad es no pasar toda la estructura a la función sino tan sólo los miembros que sean necesarios. Los ejemplos anteriores serían más correctos usando esta tercera opción, ya que sólo usamos el miembro 'edad':

int suma( char edad )
    {
    return edad+20;
    }

int main()
      {
      printf( "%s tiene ", amigo.apellido );
      printf( "%i años ", amigo.edad );
      printf( "y dentro de 20 años tendrá %i.\n", suma(amigo.edad) );
      }
Comprobado con DJGPP

Por supuesto a la función suma hay que indicarle que va a recibir una variable tipo char (amigo->edad es de tipo char).

[Arriba]

Estructuras dentro de estructuras (Anidadas)

Es posible crear estructuras que tengan como miembros otras estructuras. Esto tiene diversas utilidades, por ejemplo tener la estructura de datos más ordenada. Imaginemos la siguiente situación: una tienda de música quiere hacer un programa para el inventario de los discos, cintas y cd's que tienen. Para cada título quiere conocer las existencias en cada soporte (cinta, disco, cd), y los datos del proveedor (el que le vende ese disco). Podría pensar en una estructura así:

struct inventario {
        char titulo[30];
        char autor[40];
        int existencias_discos;
        int existencias_cintas;
        int existencias_cd;
        char nombre_proveedor[40];
        char telefono_proveedor[10];
        char direccion_proveedor[100];
        };

Sin embargo utilizando estructuras anidadas se podría hacer de esta otra forma más ordenada:

struct estruc_existencias {
       int discos;
       int cintas;
       int cd;
       };

struct estruc_proveedor {
       char nombre_proveedor[40];
       char telefono_proveedor[10];
       char direccion_proveedor[100];
       };

struct estruc_inventario {
        char titulo[30];
        char autor[40];
        struct estruc_existencias existencias;
        struct estruc_proveedor proveedor;
        } inventario;

Ahora para acceder al número de cd de cierto título usaríamos lo siguiente:

inventario.existencias.cd

y para acceder al nombre del proveedor:

inventario.proveedor.nombre

[Arriba]

[Anterior] [Siguiente] [Contenido]

© Gorka Urrutia

www.elrincondelc.com