Subsecciones

7. Más tipos de datos

En este capítulo se revisa la forma como pueden ser creados y usados en C tipos de datos más complejos y estructuras.

7.1 Estructuras

En C una estructura es una colección de variables que se referencian bajo el mismo nombre. Una estructura proporciona un medio conveniente para mantener junta información que se relaciona. Una definición de estructura forma una plantilla que se puede usar para crear variables de estructura. Las variables que forman la estructura son llamados elementos estructurados.

Generalmente, todos los elementos en la estructura están relacionados lógicamente unos con otros. Por ejemplo, se puede representar una lista de nombres de correo en una estructura. Mediante la palabra clave struct se le indica al compilador que defina una plantilla de estructura.

struct direc
{
    char nombre[30];
    char calle[40];
    char ciudad[20];
    char estado[3];
    unsigned int codigo;
};
Con el trozo de código anterior no ha sido declarada ninguna variable, tan sólo se ha definido el formato. Para declarar una variable, se hará como sigue:
struct direc info_direc;

Se pueden declarar una o más variables cuando se define una estructura entre ) y ;. Por ejemplo:

struct direc
{
    char nombre[30];
    char calle[40];
    char ciudad[20];
    char estado[3];
    unsigned int codigo;
} info_direc, binfo, cinfo;
observar que direc es una etiqueta para la estructura que sirve como una forma breve para futuras declaraciones. Como en este última declaración se indican las variables con esta estructura, se puede omitir el nombre de la estructura tipo.

Las estructuras pueden ser también preinicializadas en la declaración:

struct direc info_direc={"Vicente Fernandez","Fantasia 2000","Dorado","MMX",12345};

Para referenciar o accesar un miembro (o campo) de una estructura, C proporciona el operador punto ., por ejemplo, para asignar a info_direc otro código, lo hacemos como:

info_direc.codigo=54321;

7.1.1 Definición de nuevos tipos de datos

Se señalo previamente (sección 2.4.1) que typedef se puede usar para definir nuevos nombres de datos explicítamente, usando algunos de los tipos de datos de C, donde su formato es:

typedef <tipo> <nombre>;

Se puede usar typedef para crear nombres para tipos más complejos, como una estructura, por ejemplo:

typedef struct direc
{
    char nombre[30];
    char calle[40];
    char ciudad[20];
    char estado[3];
    unsigned int codigo;
} sdirec;

sdirec info_direc={"Vicente Fernandez","Fantasia 2000","Dorado","MMX",12345};
en este caso direc sirve como una etiqueta a la estructura y es opcional, ya que ha sido definido un nuevo tipo de dato, por lo que la etiqueta no tiene mucho uso, en donde sdirec es el nuevo tipo de datos e info_direc es una variable del tipo sdirec, la cual es una estructura.

Con C también se pueden tener arreglos de estructuras:

typedef struct direc
{
    char nombre[30];
    char calle[40];
    char ciudad[20];
    char estado[3];
    unsigned int codigo;
} info_direc;

info_direc artistas[1000];
por lo anterior, artistas tiene 1000 elementos del tipo info_direc. Lo anterior podría ser accesado de la siguiente forma:

artistas[50].codigo=22222;

7.2 Uniones

Una union es una variable la cual podría guardar (en momentos diferentes) objetos de diferentes tamaños y tipos. C emplea la sentencia union para crear uniones por ejemplo:

union numero
{
    short  shortnumero;
    long   longnumero;
    double floatnumero;
} unumero;
con lo anterior se define una unión llamada numero y una instancia de esta llamada unumero. numero es la etiqueta de la unión y tiene el mismo comportamiento que la etiqueta en la estructura.

Los miembros pueden ser accesados de la siguiente forma:

printf("%ld\n",unumero.longnumero);
con la llamada a la función se muestra el valor de longnumero.

Cuando el compilador de C esta reservando memoria para las uniones, siempre creará una variable lo suficientemente grande para que quepa el tipo de variable más largo de la unión.

Con la finalidad de que el programa pueda llevar el registro del tipo de la variable unión usada en un momento dado, es común tener una estructura (con una unión anidada) y una variable que indica el tipo de unión.

Se muestra un ejemplo de lo anterior:

typedef struct
{
    int maxpasajeros;
} jet;

typedef struct
{
    int capac_elev;
} helicoptero;

typedef struct
{
    int maxcarga;
} avioncarga;

typedef union
{
    jet jetu;
    helicoptero helicopterou;
    avioncarga  avioncargau;
} transporteaereo;

typedef struct
{
    int tipo;
    int velocidad;
    transporteaereo descripcion;
} un_transporteaereo
en el ejemplo se define una unión base de transporte aéreo el cual puede ser un jet, un helicóptero o un avion de carga.

En la estructura un_transporeaereo hay un miembro para el tipo, que indica cual es la estructura manejada en ése momento.

7.3 Conversión de tipos (casts)

C es uno de los pocos lenguajes que permiten la conversión de tipos, esto es, forzar una variable de un tipo a ser de otro tipo. Lo anterior se presenta cuando variables de un tipo se mezclan con las de otro tipo. Para llevar a cabo lo anterior se usa el operador de conversión (cast) (). Por ejemplo:

int numeroentero;
float numeroflotante = 9.87;

numeroentero = (int) numeroflotante;
con lo cual se asigna 9 a la variable numeroentero y la fracción es desechada.

El siguiente código:

int numeroentero = 10;
float numeroflotante;

numeroflotante = (float) numeroentero;
asigna 10.0 a numeroflotante. Como se observa C convierte el valor del lado derecho de la asignación al tipo del lado izquierdo.

La conversión de tipos puede ser también usada con cualquier tipo simple de datos incluyendo char, por lo tanto:

int numeroentero;
char letra = 'A';

numeroentero = (int) letra;
asigna 65 (que es el código ASCII de 'A') a numeroentero.

Algunas conversiones de tipos son hechas automáticamente - esto es principalmente por la característica de compatibilidad de tipos.

Una buena regla es la siguiente: En caso de duda, conversión de tipos.

Otro uso es asegurarse que la división de números se comporta como se requiere, ya que si se tienen dos enteros la forma de forzar el resultado a un número flotante es:

numeroflotante = (float) numerent / (float) denoment;
con lo que se asegura que la división devolverá un número flotante.

7.4 Enumeraciones

Una enumeración es un conjunto de constantes enteras con nombre y especifica todos los valores legales que puede tener una variable del tipo enum.

La forma como se define una enumeración es de forma parecida a como se hace con las estructuras, usando la palabra clave enum para el comienzo de un tipo de enumeración. Su formato es:

enum nombre_enum { lista_de_enumeración } lista_de_variables;

Es opcional nombre_enum y lista_de_variables. La primera se usa para declarar las variables de su tipo. El siguiente fragmento define una enumeración llamada disco que declara almacenamiento para ser de ese tipo.

enum almacenamiento { diskette, dd, cd, dvd, cinta };

enum almacenamiento disco;

Con la siguiente definición y declaración son válidas las siguientes sentencias:

disco = cd;

if ( disco == diskette ) 
    printf("Es de 1440 Kb\n");

Se inicializa el primer símbolo de enumeración a cero, el valor del segundo símbolo a 1 y así sucesivamente, a menos que se inicialice de otra manera. Por tanto,

printf("%d %d\n", dd, cinta)
muestra 1 4 en la pantalla.

Se puede especificar el valor de uno o más símbolos usando un inicializador. Para hacerlo, poner un signo igual y un valor entero después del símbolo.

Por ejemplo, lo siguiente asigna el valor 250 a cd

enum disco { diskette, duro, cd=250, dvd, cinta };

Por lo tanto los valores de los símbolos son los siguientes:

diskette 0
duro 1
cd 250
dvd 251
cinta 252

7.5 Variables estáticas

C soporta cuatro especificadores de clase de almacenamiento. Son

auto
extern
static
register

Estos especifcadores le dicen al compilador como almacenar la variable que sigue. El especificador de almacenamiento precede a la declaración de variable que tiene el formato general:

especif_almac especif_tipo lista_variables;

Se usa el especificador auto para declarar variables locales. Sin embargo, raramente se usa porque las variables locales son auto por defecto.

Las variables static son variables permanentes en su propia función o archivo. Se diferencian de las variables globales porque son desconocidas fuera de sus funciones o archivo, pero mantienen sus valores entre llamadas. Esta característica las hace útiles cuando se escriben funciones generales y bibliotecas de funciones.

Variables static locales. Una variable estática local es una variable local que retienen su valor entre llamadas de función, ya que C les crea un almacenamiento permanente. Si lo anterior no se pudiera hacer, entonces se tendrían que usar variables globales -que abriría la puerta a posibles efectos laterales-. Un ejemplo de una función que requeriría tales variables es un generador de series de números que produce un número nuevo basado en el último.

A continuación se muestra un ejemplo en donde se analiza el comportamiento de una variable auto y una variable static

void stat();   /* Prototipo de la funcion */

main()
{
    int i;
    for (i=0; i<5; ++i)
        stat();
}

void stat()
{
    auto int a_var = 0;
    static int s_var = 0;

    printf("auto = %d, static = %d \n", a_var, s_var);
    ++a_var;
    ++s_var;
}

La salida del código anterior es:

auto = 0, static = 0
auto = 0, static = 1
auto = 0, static = 2
auto = 0, static = 3
auto = 0, static = 4

Como se puede observar la variable a_var es creada cada vez que se llama a la función. La variable s_var es creada en la primera llamada y después recuerda su valor, es decir, no se destruye cuando termina la función.

7.6 Ejercicios

  1. Escribir un programa que use el tipo enumerate para mostrar el nombre de un mes, su predecesor y su sucesor. El mes se ingresará desde el teclado dando un número entre 1 y 12.
  2. Escribir un programa que contenga una función que pueda recibir dos estructuras que contienen las coordenadas en el plano de dos puntos dados, de los cuales se desea conocer su punto medio.