Versión 3.0

800x600 mínimo
En esta lección:

¿Qué es una estructura?
-----
Un array de estructuras
-----
Estructuras y punteros
-----
Estructuras anidadas
-----


Uniones Otras secciones:

Conceptos básicos
-----
Programando en C
-----
Programando en C++
-----
Programando Windows 9x.
-----
Teoría electrónica
-----
Circuitos electrónicos
-----
Actividades adicionales
-----
Hipervínculos
-----


Contácteme:

Dudas y comentarios
-----


Hola mundo

¿Qué es una estructura?

Una estructura es un tipo de dato definido por el usuario, al utilizar una estructura Usted tiene la habilidad para definir un nuevo tipo de dato considerablemente más complejo que los tipos que hemos utilizado hasta ahora. Una estructura es una combinación de varios tipos de datos previamente definidos, incluyendo otras estructuras que hayamos definido previamente. Una definición simple es, "una estructura es un grupo de datos relacionados en una forma conveniente al programador y/o al usuario del programa". Como es costumbre, un ejemplo nos clarifica los conceptos:


#include <stdio.h>

struct
{
    char inicial;       /* Letra inicial del apellido */
    int  edad;          /* Edad */
    int  calificacion;  /* Aprovechamiento */
}
chico, chica;

int main()
{
    chico.inicial = 'R';
    chico.edad = 15;
    chico.calificacion = 75;
    chica.edad = chico.edad - 1; /* Ella es un año menor que él */
    chica.calificacion = 82;
    chica.inicial = 'H';

    printf("%c tiene %d anos y su calificacion es de %d\n",
      chica.inicial, chica.edad, chica.calificacion);
    printf("%c tiene %d anos y su calificacion es de %d\n",
      chico.inicial, chico.edad, chico.calificacion);

    return 0;
}

El programa empieza definiendo una estructura utilizando la palabra clave struct seguida de tres variables sencillas encerradas entre llaves, las cuales son los componentes de la estructura, despues de la llave de cierre tenemos enlistadas dos variables llamadas chico y chica. De acuerdo a la definición de una estructura, chico es una variable compuesta de tres elementos, inicial, edad y, calificacion. Cada uno de los tres campos están asociados a chico y cada uno almacena una variable de su respectivo tipo, lo mismo se puede decir para chica pero sus variables son diferentes por lo tanto tenemos 6 variables agrupadas en dos, de tipo struct.

Una variable compuesta

Examinemos la variable llamada chico más carcanamente, como ya mencionamos, cada uno de los tres elementos de chico son simples variables y pueden ser utilizadas como cualquier otra, por ejemplo, el elemento edad es una variable de tipo int que puede ser utilizada en cálculos, como contador, en operaciones de E/S, etc. Tenemos ahora el problema de definir cómo usar la variable llamada edad que es parte de la variable compuesta llamada chico, para esto utilizamos ambos nombres separados por un punto decimal con el nombre principal en primer término, entonces, chico.edad es el nombre completo para el campo edad de chico, este enunciado puede utilizarse en cualquier parte del programa C si deseamos referirnos a éste campo. De hecho, es ilegal utilizar el nombre chico ó edad individualmente porque son definiciones parciales de un campo.

Asignando valores a las variables

Usando la definición dada arriba, podemos asignar un valor a cada uno de los tres campos de chico e igualmente para chica, observe que chico.inicial es una variable de tipo char ya que así fué definida en la estructura por lo que se le debe asignar un caracter. En la línea 13 asignamos el caracter R a chico.inicial de acuerdo a las reglas en tanto que a los otros dos campos de chico se les asigna valores de acuerdo a sus respectivos tipos. Finalmente asignamos valores a los tres campos de chica pero en diferente orden para ilustrar que ésto no es crítico, observe que se utiliza la edad del chico para determinar la edad de la chica, esto ilustra el uso de un miembro de la estructura.

Volver al principio

Hola mundo

Un array de estructuras

El siguiente programa es básicamente el mismo que el anterior, pero esta vez definimos un array de 12 variables llamadas chicos, está claro que éste programa contiene 12 veces 3=36 variables sencillas cada una de las cuales puede almacenar un ítem de dato siempre y cuando sea del tipo adecuado, se define además una variable común llamada indice para utilizarla en los bucles, estudie el código:


#include <stdio.h>

struct
{
    char inicial;
    int  edad;
    int  calificacion;
}
chicos[12];

int main()
{
    int indice;

    for (indice = 0; indice < 12; indice++)
    {
        chicos[indice].inicial = 'A' + indice;
        chicos[indice].edad = 16;
        chicos[indice].calificacion = 84;
    }

    chicos[3].edad = chicos[5].edad = 17;
    chicos[2].calificacion = chicos[6].calificacion = 92;
    chicos[4].calificacion = 57;

    /* Asignacion de estructura solo en compiladores ANSI-C */
    chicos[10] = chicos[4];

    for (indice = 0; indice < 12; indice++)
    printf("%c tiene %d anos y una calificacion de %d\n",
      chicos[indice].inicial, chicos[indice].edad,
      chicos[indice].calificacion);

    return 0;
}

Para asignar un valor a cada uno de los campos utilizamos un bucle for, en cada ciclo del bucle se asignan todos los valores para uno de los chicos, en una situación real ésta podría no ser la mejor manera de asignar datos, pero un bucle puede leer los datos de un archivo y almacenarlos en la correcta ubicación en un programa real, considere éste ejemplo como un inicio burdo a una base da datos, pues eso es justamente nuestro ejemplo. El código resulta fácil de entender, solo haré un comentario respecto a la línea 26 en donde podemos ver una asgnación de estructura, en éste enunciado los tres campos de chicos[4] son copiados en los respectivos campos de chicos{10], esto no siempre está permitido en el lenguaje C, solo en los compiladores que cumplen con la norma ANSI-C, si su compilador no es ANSI-C encierre en comentarios la línea 26. El resultado de la ejecución delprograma es el siguiente:

Una base de datos

Volver al principio

Hola mundo

Estructuras y punteros

Ahora modificamos nuevamente el programa del ejemplo anterior para utilizar punteros en algunas de las operaciones, la primera diferencia se muestra en la definición de las variables enseguida de la definiciónde la estructura, tenemos un puntero llamado puntero el cual señala a la estructura, sería ilegal tratar de utilizar éste puntero para señalar a cualquier otro tipo de variable por una fuerte razón que estudiaremos un poco más adelante en ésta misma lección, entre tanto le presento el código:


#include <stdio.h>

struct
{
    char inicial;
    int  edad;
    int  calificacion;
}
chicos[12], *puntero, extra;

int main()
{
    int indice;

    for (indice = 0; indice < 12; indice++)
    {
        puntero = chicos + indice;
        puntero->inicial = 'A' + indice;
        puntero->edad = 16;
        puntero->calificacion = 84;
    }

    chicos[3].edad = chicos[5].edad = 17;
    chicos[2].calificacion = chicos[6].calificacion = 92;
    chicos[4].calificacion = 57;

    for (indice = 0; indice < 12; indice++)
    {
        puntero = chicos + indice;
        printf("%c tiene %d anos y su calificacion es %d\n",
          (*puntero).inicial, chicos[indice].edad,
          puntero->calificacion);
    }

    extra = chicos[2];      /* Asignacion de estructura */
    extra = *puntero;       /* Asignacion de estructura */

    return 0;
}

La siguiente diferencia la encontramos en el bucle for donde utilizamos el puntero para acceder a los campos de datos, recuerde que el nombre de un array es en realidad un puntero al primer elemento del array, como chicos es un puntero constante que señala al primer elemento del array el cual es una estructura, podemos definir a puntero en términos de chicos. El elemento llamado chicos es constante por lo que no puede alterarse su valor, pero puntero es una variable puntero que se le puede asignar cualquier valor consistente requerido para señalar a la estructura. Si asignamos el valor de chicos a puntero entonces está claro que puntero además señalará al primer elemento del array que es una estructura que contiene tres campos. Es muy útil para comprender el funcionamiento del programa utilizar un debbuger que nos permita ejecutar el programa paso a paso.

Aritmética de punteros

Sumando 1 a puntero causará que señale al segundo campo del array debido a la manera en que los punteros son manejados en C. El sistema sabe que la estructura contiene tres variables y sabe cuántos elementos de memoria son requeridos para almacenar la estructura completa, por tanto si le indicamos al sistema que agregue 1 al puntero, agregará los elementos de memoria necesarios para obtener el siguiente elemento del array. Si, por ejemplo, sumamos 4 al puntero, el sistema avanzará el valor del puntero 4 veces el tamaño de la estructura dando como resultado que el puntero señale 4 elementos más allá del array. Esta es la razón por la cual un puntero no puede utilizarse para señalar a otro tipo de dato excepto al que fué definido.

Del párrafo anterior está claro que conforme avanzamos en el bucle el puntero señalará a uno de los elementos del array en cada ciclo, podemos por lo tanto utilizar el puntero para referenciar a varios elementos de cada una de las estructuras conforme avanzamos por el bucle. Referenciar a los elementos de una estructura con un puntero ocurre tan a menudo en C que se utiliza una notación especial. Utilizar puntero->inicial es lo mismo que utilizar (*puntero).inicial lo cual es lo que hicimos en el programa. El símbolo de "->" se hace con el signo de menos y el de mayor que.
Como el puntero señala a la estructura, debemos definir una vez más cuál de los elementos deseamos referenciar cada vez que utilizamos uno de los elementos de la estructura. Existen, como podemos ver, varios métodos diferentes para referirnos a los miembros de la estructura. Cuando ejecutamos el bucle for para desplegar los datos al final del programa, utilizamos tres métodos diferentes para referenciar los elementos de la estructura. Esto puede considerarse una práctica pobre de programación pero la utilizamos para fines de ilustración.

Volver al principio

Hola mundo

Estructuras anidadas

El siguiente ejemplo muestra una estructura anidada. Las estructuras que hemos visto han sido muy sencillas aunque útiles. Es posible definir estructuras conteniendo docenas y aún cientos ó miles de elementos pero sería ventajoso para el programador no definir todos los elementos en una pasada sino utilizar una definición de estructura jerárquica.


#include <string.h>

struct persona
{
    char nombre[25];
    int  edad;
    char estado;     /* C = casado, S = soltero */
};

struct datos
{
    int calificacion;
    struct persona descripcion;
    char comida[25];
};

int main()
{
    struct datos estudiante[53];
    struct datos maestro, sub;

    maestro.calificacion = 94;
    maestro.descripcion.edad = 23;
    maestro.descripcion.estado = 'M';
    strcpy(maestro.descripcion.nombre, "Lisseth Gil");
    strcpy(maestro.comida, "Chocolates de Ron");

    sub.descripcion.edad = 87;
    sub.descripcion.estado = 'M';
    strcpy(sub.descripcion.nombre, "Abuela Pata");
    sub.calificacion = 73;
    strcpy(sub.comida, "Maiz y agua");

    estudiante[1].descripcion.edad = 15;
    estudiante[1].descripcion.estado = 'S';
    strcpy(estudiante[1].descripcion.nombre, "Bill Griton");
    strcpy(estudiante[1].comida, "Crema de cacahuate");
    estudiante[1].calificacion = 77;

    estudiante[7].descripcion.edad = 14;
    estudiante[12].calificacion = 87;

    return 0;
}

La primera estructura contiene tres elementos pero no le sigue ninguna variable definida, sólo una estructura, pero como incluimos un nombre al principio de la estructura, la estructura es llamada persona. La palabra persona puede utilizarse para referirse a la estructura pero no a cualquier variable de éste tipo de estructura, se trata por lo tanto de un nuevo tipo que hemos definido y lo podemos utilizar de la misma manera en que usamos un int, char o cualquier otro tipo que existe en C. La única restricción es que éste nuevo tipo debe estar siempre asociado con la palabra clave struct.

La siguiente definición de estructura contiene tres campos siendo el segundo la estructura previamente definida la cual llamamos persona. La variable de tipo persona se llama descripcion, así la nueva estructura contiene dos variables simples, calificacion y una cadena llamada comida, y la estructura llamada descripcion. Como descripcion contiene tres variables, la nueva estructura tiene entonces cinco variables, a ésta estructura le hemos dado el nombre de datos, lo cual es otro tipo definido. Finalmente, dentro de la función main ( ) definimos un array de 53 variables cada una con la estructura definida por el tipo datos, y cada una con el nombre estudiante, en total hemos definido 53 veces 5 variables, cada una de las cuales es capaz de almacenar datos. Como tenemos la definición de un nuevo tipo podemos utilizarla para a su vez definir dos variables. Las variables maestro y sub están definidas en la línea 20 como variables de tipo datos por lo que cada una de éstas dos variables contienen 5 campos en los cuales podemos almacenar datos.

En las líneas 22 a 26 del programa asignamos valores a cada uno de los campos de maestro. El primero es el campo calificacion y es manejado como las otras estructuras que hemos estudiado porque no forma parte de la estructura anidada. Enseguida deseamos asignar un valor a edad el cual es parte de la estructura anidada. Para acceder a éste campo empezamos con el nombre de la variable maestro al cual le concatenamos el nombre del grupo descripcion, y entonces debemos definir en cuál campo de la estructura anidada estamos interesados por lo que concatenamos el nombre de la variable edad. El estado de los maestros se manejan de la misma manera que su edad pero los últimos dos campos son cadenas asignadas utilizando la función strcpy ( ). Observe que los nombres de las variables en la función strcpy ( ) se consideran como una unidad aunque estén compuestas de varias partes.

Compile y ejecute el programa, probablemente obtenga un aviso sea de error ó advertencia respecto a un desbordamiento de memoria similar a éste:

No hay suficiente memoria

Lo que ésto significa es que el programa requiere más memoria que la asignada por el compilador por lo que es necesario incrementar el tamaño de stacks, el método para hacer ésto varía de un compilador a otro, en el caso concreto del compilador de Symantec que utilizo para los programas de éste tutorial, se cumple el objetivo asignando un modelo de memoria mayor:

Modelo de memoria

Volver al principio

Hola mundo

Uniones

Dicho de una forma simple, una unión le permite manejar los mismos datos con diferentes tipos, ó utilizar el mismo dato con diferente nombre, a continuación le presento un ejemplo:


#include <stdio.h>

int main()
{
    union
    {
        int valor; /* Esta es la primera parte de la union */

        struct
        {
            char primero; /* Esta es la segunda parte */
            char segundo;
        }
        mitad;
    }
    numero;

    long indice;
    for (indice = 12; indice < 300000L; indice += 35231L)
    {
        numero.valor = indice;
        printf("%8x %6x %6x\n", numero.valor,
          numero.mitad.primero, numero.mitad.segundo);
    }
    return 0;
}

En éste ejemplo tenemos dos elementos en la unión, la primera parte es el entero llamado valor el cual es almacenado en algún lugar de la memoria de la computadora como una variable de dos bytes. El segundo elemento está compuesto de dos variables de tipo char llamadas primero y segundo. Estas dos variables son almacenadas en la misma ubicación de almacenamiento que valor porque ésto es precisamente lo que una unión hace, le permite almacenar diferentes tipos de datos en la misma ubicación física. En éste caso Usted puede poner un valor de tipo entero en valor y después recobrarlo en dos partes utilizando primero y segundo, ésta técnica es utilizada a menudo para empaquetar bytes cuando, por ejemplo, combine bytes para utilizarlos en los registros del microprocesador.
La unión no es utilizada frecuentemente y casi nunca por programadores principiantes, en este momento no necesita profundizar en el empleo de la unión así que no dedique mucho tiempo a su estudio, sin embargo no tome a la ligera el concepto de la unión, podría utilizarlo a menudo.

¿Qué es un campo de bits?

Para finalizar la presente lección estudiaremos el concepto de campo de bits, en el siguiente código podemos ver la manera de definirlo y utilizarlo, en el programa tenemos una unión de una variable sencilla de tipo int en la línea 5 y la estructura definida en las líneas 6 a la 12:


#include <stdio.h>

union
{
    int indice;
    struct
    {
        unsigned int x : 1;
        unsigned int y : 2;
        unsigned int z : 2;
    }
    bits;
}
numero;

int main()
{
    for (numero.indice = 0; numero.indice < 20; numero.indice++)
    {
        printf("indice = %3d, bits = %3d%3d%3d\n", numero.indice,
          numero.bits.z, numero.bits.y, numero.bits.x);
    }

    return 0;
}

La estructura está compuesta de tres campos de bits llamados x, y, y z. La variable llamada x es de un solo bit, la variable y es de dos bits y es adyacente a la variable x, y la variable z es de dos bits y adyacente a la variable y. Como la unión causa que los bits sean almacenados en la misma ubicación en memoria que la variable indice, la variable x es el bit menos significante de la variable indice, y conforma los siguientes dos bits, y z es almacenado en los siguientes dos bits de indice. Compile y ejecute el programa y verá que al ser incrementada la variable indice en cada ciclo del bucle, verá los campos de bits incrementarse en sus respectivas ubicaciones. Una cosa debemos señalar, los campos de bits deben definirse como partes de un tipo unsigned int de lo contrario el compilador marcará error. El resultado de la ejecución del programa es:

Campos de bits

Volver al principio

Hola mundo

© 1998, 1999 Virgilio Gómez Negrete, Derechos Reservados