ESTRUCTURAS

DECLARACION DE ESTRUCTURAS
Así como los arrays son organizaciones secuenciales de variables simples , de un mismo tipo cualquiera dado , resulta necesario en multiples aplicaciones , agrupar variables de distintos tipos , en una sola entidad . Este sería el caso , si quisieramos generar la variable " legajo personal " , en ella tendriamos que incluir variables del tipo : strings , para el nombre , apellido , nombre de la calle en donde vive , etc , enteros , para la edad , número de codigo postal , float ( ó double , si tiene la suerte de ganar mucho ) para el sueldo , y así siguiendo . Existe en C en tipo de variable compuesta , para manejar ésta situación típica de las Bases de Datos , llamada ESTRUCTURA . No hay limitaciones en el tipo ni cantidad de variables que pueda contener una estructura , mientras su máquina posea memoria suficiente como para alojarla , con una sóla salvedad : una estructura no puede contenerse a sí misma como miembro .
Para usarlas , se deben seguir dos pasos . Hay que , primero declarar la estructura en sí , ésto es , darle un nombre y describir a sus miembros , para finalmente declarar a una ó más variables , del tipo de la estructura antedicha , veamos un ejemplo :

struct legajo {

                 int edad ;

                 char nombre[50] ;

                 float sueldo ;

               } ;

struct legajo  legajos_vendedores , legajos_profesionales ;

En la primer sentencia se crea un tipo de estructura , mediante el declarador "struct",luego se le dá un nombre " legajo " y finalmente , entre llaves se declaran cada uno de sus miembros , pudiendo estos ser de cualquier tipo de variable , incluyendo a los arrays ó aún otra estructura . La única restricción es que no haya dos miembros con el mismo nombre , aunque sí pueden coincidir con el nombre de otra variable simple , ( o de un miembro de otra estructura ) , declaradas en otro lugar del programa. Esta sentencia es sólo una declaración , es decir que no asigna lugar en la memoria para la estructura , sólo le avisa al compilador como tendrá que manejar a dicha memoria para alojar variables del tipo struct legajo .
En la segunda sentencia , se definen dos variables del tipo de la estructura anterior ,(ésta definición debe colocarse luego de la declaración ) , y se reserva memoria para ambas .
Las dos sentencias pueden combinarse en una sola , dando la definición a continuación de la declaracion :

struct legajo {

                 int edad ;

                 char nombre[50] ;

                 float sueldo ;

               }   legajo_vendedor , legajo_programador ;

 

Y si nó fueran a realizarse más declaraciones de variables de éste tipo , podría obviarse el nombre de la estructura ( legajo ).

Las variables del tipo de una estructura , pueden ser inicializadas en su definición , así por ejemplo se podría escribir:

struct legajo {

                 int edad ;

                 char nombre[50] ;

                 float sueldo ;

                 char observaciones[500] ;

               }   legajo_vendedor = { 40 , "Juan Eneene" , 1200.50 ,

                                          "Asignado a zona A"        } ;
struct legajo  legajo_programador = { 23 , "Jose Peres" , 2000.0 ,

                                     "Asignado a zona B" } ;

Acá se utilizaron las dos modalidades de definición de variables , inicializandolas a ambas .

REGLAS PARA EL USO DE ESTRUCTURAS
Lo primero que debemos estudiar es el método para dirigirnos a un miembro particular de una estructura .Para ello existe un operador que relaciona al nombre de ella con el de un miembro , este operador se representa con el punto ( . ) , así se podrá referenciar a cada uno de los miembros como variables individuales , con las particularidades que les otorgan sus propias declaraciones , internas a la estructura.
La sintaxis para realizar ésta referencia es : nombre_de_la_estructura.nombre_del_miembro , así podremos escribir por ejemplo , las siguientes sentencias

strut posicion_de {
float eje_x ;
float eje_y ;
float eje_z ;
} fin_recta , inicio_recta = { 1.0 , 2.0 , 3.0 ) ;

fin_recta.eje_x = 10.0 ;
fin_recta.eje_y = 50.0 ;
fin_recta.eje_z = 90.0 ;

if( fin_recta.eje_x == inicio_recta.eje_x )
..........................................

Es muy importante recalcar que , dos estructuras , aunque sean del mismo tipo , no pueden ser asignadas ó comparadas la una con la otra , en forma directa , sino asignando ó comparandolas miembro a miembro. Esto se vé claramente explicitado en las líneas siguientes , basadas en las declaraciones anteriores:

fin_recta = inicio_recta ;             /* ERROR */

if( fin_recta >>= inicio_recta );       /* ERROR */

fin_recta.eje_x = inicio_recta.eje_x ;     /* FORMA CORRECTA DE ASIGNAR */

fin_recta.eje_y = inicio_recta.eje_y ;     /* UNA ESTRUCTURA A OTRA     */

fin_recta.eje_z = inicio_recta.eje_z ;
if( (fin_recta.eje_x >>= inicio_recta.eje_x) &&     /* FORMA CORRECTA DE  */

    (fin_recta.eje_y >>= inicio_recta.eje_y) &&     /* COMPARAR UNA       */

    (fin_recta.eje_z >>= inicio_recta.eje_z) )      /* ESTRUCTURA CON OTRA */

Las estructuras pueden anidarse , es decir que una ó mas de ellas pueden ser miembro de otra . Las estructuras también pueden ser pasadas a las funciones como parámetros , y ser retornadas por éstas , como resultados .

ARRAYS DE ESTRUCTURAS
Cuando hablamos de arrays dijimos que se podían agrupar , para formarlos , cualquier tipo de variables , esto es extensible a las estructuras y podemos entonces agruparlas ordenadamente , como elementos de un array . Veamos un ejemplo :

typedef struct {

                 char     material[50] ;

                 int        existencia ;

                 double costo_unitario ;

                } Item ;
Item  stock[100] ;

Hemos definido aquí un array de 100 elementos , donde cada uno de ellos es una estructura del tipo Item compuesta por tres variables , un int , un double y un string ó array de 50 caracteres.

Los arrays de estructuras pueden inicializarse de la manera habitual , así en una definición de stock, podríamos haber escrito:

Item   stock1[100] = {

                       "tornillos"  , 120 , .15 ,

                       "tuercas"    , 200 , .09 ,

                       "arandelas"  ,  90 , .01

                      } ;

Item   stock2[]     = {

                        { 'i','t','e','m','1','\0' } , 10 , 1.5 ,

                        { 'i','t','e','m','2','\0' } , 20 , 1.0 ,

                        { 'i','t','e','m','3','\0' } , 60 , 2.5 ,

                        { 'i','t','e','m','4','\0' } , 40 , 4.6 ,

                        { 'i','t','e','m','5','\0' } , 10 , 1.2 ,

                       } ;

Analicemos un poco las diferencias entre la dos inicializaciones dadas , en la primera , el array material[] es inicializado como un string , por medio de las comillas y luego en forma ordenada , se van inicializando cada uno de los miembros de los elementos del array stock1[] , en la segunda se ha preferido dar valores individuales a cada uno de los elementos del array material , por lo que es necesario encerrarlos entre llaves .
Sin embargo hay una diferencia mucho mayor entre las dos sentencias , en la primera explicitamos el tamaño del array , [100] , y sólo inicializamos los tres primeros elementos , los restantes quedarán cargados de basura si la definición es local a alguna función , ó de cero si es global , pero de cualquier manera están alojados en la memoria , en cambio en la segunda dejamos implícito el número de elementos , por lo que será el compilador el que calcule la cantidad de ellos , basandose en cuantos se han inicializado , por lo tanto este array sólo tendrá ubicados en memoria cuatro elementos , sin posibilidad de agregar nuevos datos posteriomente .
Veremos más adelante que en muchos casos es usual realizar un alojamiento dinámico de las estructuras en la memoria , en razón de ello , y para evitar ademas el saturación de stack por el pasaje ó retorno desde funciones , es necesario conocer el tamaño , ó espacio en bytes ocupados por ella .
Podemos aplicar el operador sizeof , de la siguiente manera :

longitud_base_de_datos  = sizeof( stock1 ) ;

longitud_de_dato         = sizeof( Item )  ;

cantidad_de_datos        = sizeof( stock1 ) / sizeof( Item ) ;

Con la primera calculamos el tamaño necesario de memoria para albergar a todos datos, en la segunda la longitud de un sólo elemento ( record ) y por supuesto dividiendo ambas , se obtiene la cantidad de records.

UNIONES
Las uniones son a primera vista, entidades muy similares a las estructuras, están formadas por un número cualquiera de miembros, al igual que aquellas, pero en éste caso no existen simultaneamente todos los miembros, y sólo uno de ellos tendrá un valor válido.
Supongamos por caso, que queremos guardar datos para un stock de materiales , pero los mismos pueden ser identificados , en un caso con el número de articulo (un entero ) y en otro por su nombre ( un string de 10 letras como máximo ). No tendría sentido definir dos variables , un int y un string , para cada artículo , ya que voy a usar una modalidad ú la otra, pero nó las dos simultaneamente. Las uniones resuelven este caso , ya que si declaro una que contenga dos miembros, un entero y un string , sólo se reservará lugar para el mayor de ellos , en estee caso, el string , de tal forma que si asigno un valor a éste se llenará ese lugar de la memoria con los caracteres correspondientes, pero si en cambio asigno un valor al miembro declarado como int éste se guardará en los dos primeros bytes del MISMO lugar de memoria. Por supuesto, en una unión, sólo uno de los miembros tendrá entonces un valor correcto .