You are on page 1of 38

Programación II

Tipos de dato definidos por el usuario -1-

TIPOS DEFINIDOS POR EL USUARIO


Estructura................................................................................................................................................................. 1
Declaración de estructuras................................................................................................................................... 2
Definición de variables estructura........................................................................................................................3
Estructuras anidadas............................................................................................................................................ 4
Referencia a elementos individuales..................................................................................................................... 4
Iniciación de una estructura en su definición....................................................................................................... 5
Arrays de estructuras............................................................................................................................................ 6
Punteros y estructuras.......................................................................................................................................... 7
Operaciones con estructuras como unidades....................................................................................................... 9
Campos de bits........................................................................................................................................................ 10
Uniones................................................................................................................................................................... 12
Ejemplo práctico de utilización de un campo de bits......................................................................................... 13
Enumeraciones.......................................................................................................................................................17
Referencia a las variables enumeración............................................................................................................. 19
typedef..................................................................................................................................................................... 21
Ejercicios resueltos................................................................................................................................................ 23
Programas complementarios................................................................................................................................. 35
Ejercicios propuestos............................................................................................................................................. 36
................................................................................................................................................................................38

Estructura
Los datos simples pueden almacenar un único elemento de información. Los vectores y las
matrices pueden almacenar un cierto número de elementos de información del
mismo tipo de dato. Pero frecuentemente es necesario almacenar en una única
unidad, datos de diferentes tipos, para lo cual C proporciona un tipo especial de
dato: la estructura.

Una estructura consta de un número de elementos de datos, que no necesitan ser del mismo
tipo, agrupados dentro de la misma entidad. En otros lenguajes se les suele
denominar registro o record.

Los datos individuales que forman la estructura se denominan elementos o miembros de la


estructura. Los miembros de una estructura normalmente están relacionados de una forma
lógica desde el punto de vista de la información que tratan.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario -2-

Declaración de estructuras
Mediante la palabra reservada struct se establece un patrón, plantilla o tipo de la estructura
que se va a emplear. La forma general de declarar el tipo de una estructura es:
struct nombre_tipo_estructura {
tipo1 miembro1;
tipo2 miembro2;
...
tipon miembron;
};
donde:
 struct es una palabra reservada requerida.
 nombre_tipo_estructura es un identificador para dar nombre a la estructura declarada.
 tipox miembrox son las declaraciones de los miembros individuales de la estructura.
 Es necesaria la inclusión de un punto y coma (;) después de cerrar las llaves.

Los nombres de los miembros dentro de una estructura particular deben ser diferentes, pero
pueden coincidir con el nombre de variables fuera de la estructura o con los nombres de los
miembros de otras estructuras.

La declaración de un tipo de estructura crea un nuevo tipo de dato definido por el


programador. Pero no ocupa memoria, ya que no se produce reserva de la misma hasta que
no se declara una variable de dicho tipo.

Por ejemplo, la siguiente será la declaración de una estructura alumno para relacionar los
datos correspondientes a un alumno de primer curso. Esta estructura contiene el nombre, la
dirección, el número de matrícula, el teléfono y las notas de las 10 asignaturas.
struct alumno { alumno (estructura)
char nombre [51];
char direccion [81]; nombre (miembro)
unsigned long num_matricula; direccion (miembro)
unsigned long telefono; num_matricula (miembro)
float notas[10];
};
telefono (miembro)
notas (miembro)

Figura 1. Representación gráfica del tipo estructura alumno y sus miembros

Los miembros individuales de la estructura pueden ser de cualquier tipo, incluso punteros,
matrices u otras estructuras.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario -3-

Definición de variables estructura


Existen dos formas:

A) En la declaración del tipo de la estructura, especificando los nombres de las variables


estructuras después de la llave de cierre.
struct nombre_tipo_estructura {
tipo1 miembro1;
tipo2 miembro2;
...
tipon miembron;
} nomvar1, nomvar2, ... ;

En este caso es posible omitir, si se desea, el nombre del tipo estructura, por lo que también
sería válida la declaración:
struct { tipo1 miembro1;
tipo2 miembro2;
...
tipon miembron;
} nomvar1, nomvar2, ... ;

Si se omite el nombre del tipo estructura, se dice que se está declarando variables estructura
sin etiqueta. En este caso no se puede volver a definir otras variables de este tipo de
estructura, pues no existe nombre para el tipo.

B) Después de que el tipo de la estructura ha sido declarado, pueden definirse variables de


ese tipo de estructura como si se tratase de un tipo predefinido.
struct nombre_tipo_estructura nomvar1, nomvar2, ... ;

En la declaración de las variables es necesario especificar struct nombre_tipo_estructura y


no nombre_tipo_estructura simplemente

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario -4-

Estructuras anidadas
Una variable estructura puede ser definida como miembro de otra estructura. En tales
situaciones, la declaración de la estructura interna debe aparecer antes que la declaración de la
estructura externa.

Por ejemplo,
struct fecha {
int dia;
int mes;
int anio;
};

struct cuenta {
unsigned long numero;
char tipo;
char titular[51];
float saldo;
struct fecha fapertura;
};

struct cuenta nuevocliente, antiguocliente;

La segunda estructura cuenta contiene otra estructura fecha como uno de sus miembros, por
lo que la declaración de la estructura fecha debe preceder a la de la estructura cuenta.

Referencia a elementos individuales


Los miembros de una variable estructura pueden procesarse de modo individual, como
entidades separadas, por lo que debe ser posible acceder a los miembros individuales de una
variable estructura.
Una forma de acceder a ellos es a través del operador miembro de una estructura, que se
presenta por un punto (.), precedido por el nombre de la variable estructura y seguido por el
nombre del miembro.
variable.miembro

Un miembro de una variable estructura referenciado de esta forma se comportará como


cualquier variable ordinaria del tipo declarado para ese miembro de la estructura.

Por ejemplo, en la variable estructura nuevocliente declarada anteriormente, son válidas


sentencias del tipo:

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario -5-

nuevocliente.numero = 3130786;
gets(nuevocliente.titular);
strcpy(nuevocliente.titular, "Juan López Aguilrre");
nuevocliente.fapertura.dia = 17;
nuevocliente.fapertura.anio = 2003;

El operador miembro de una estructura (.) pertenece al grupo de mayor prioridad, por lo
que se evalúa primero que cualquier otro operador de menor prioridad.

Por ello, la expresión &variable.miembro es equivalente a &(variable.miembro), por lo


que la expresión anterior accede a la dirección de comienzo del miembro de la variable
estructura y no a la dirección de comienzo de la variable estructura.

Del mismo modo, la expresión ++variable.miembro es equivalente a ++


(variable.miembro), por lo que el operador ++ se aplica sobre el miembro de la variable
estructura y no sobre la variable estructura completa.

Iniciación de una estructura en su definición


A los miembros de una variable estructura se les puede asignar valores iniciales en su
definición. Los valores iniciales deben aparecer en el mismo orden en que serán asignados a
los correspondientes miembros de la variable estructura, encerrados entre llaves y separados
por comas. El siguiente ejemplo ilustra la iniciación de variables estructuras.
struct fecha {
int dia;
int mes;
int anio;
};
struct cuenta {
unsigned long numero;
char tipo;
char titular[51];
float saldo;
struct fecha fapertura;
};
struct cuenta nuevocliente = {30056231452, 'C',
"SANTIAGO DENIA", 365800, 12, 5, 1989};

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario -6-

Arrays de estructuras
Es posible declarar arrays de estructuras, es decir, vectores o tablas en las que cada
elemento sea una estructura.
struct alumno {
char nombre [51];
char direccion [81];
unsigned long num_matricula;
unsigned long telefono;
float notas[10];
};

struct alumno clase_primero[100];

En este ejemplo, clase_primero es un vector de estructuras con espacio para almacenar los
datos de 100 alumnos. Cada elemento de clase_primero es una estructura de tipo alumno
(cada elemento de clase_primero representa un registro de alumno individual).

El operador subíndice de una tabla [ ], tiene la misma prioridad que el operador miembro
de una estructura (.) y su asociatividad es de izquierda a derecha.

Por ello, en el caso de vectores miembros de una estructura,


struct {
int miembro [N];
} variable;

la expresión variable.miembro[i] es equivalente a (variable.miembro)[i], por lo que se


está accediendo a elemento i del vector miembro de la variable estructura.

Del mismo modo, en el caso de vectores de estructuras


struct {
int miembro;
} variable[M];

la expresión variable[k].miembro es equivalente a (variable[k]).miembro, por lo que se


está accediendo al miembro del elemento k del vector de estructuras.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario -7-

Pueden asignarse valores iniciales en la declaración de una matriz de estructuras, como


cualquier otra matriz.
struct fecha {
int dia;
int mes;
int anio;
};
struct persona {
char nombre[81];
struct fecha fnacimiento;
} agenda[] = {"ANA", 30, 12, 1973,
"JOSE", 13, 5, 1966,
"ROSA", 4, 2, 1977,
"SARA", 29, 7, 1963,
"ANTONIO", 31, 3, 1974};

En este ejemplo, agenda es un vector de estructuras cuyo tamaño está sin especificar. El
número de valores de iniciación definirá, por tanto, el tamaño del vector.
Cada fila de los valores de iniciación contiene 4 constantes. Estas constantes representan los
valores iniciales, eso es, un nombre, un día, un mes y un año para un elemento del vector.
Como hay 5 conjuntos de constantes (5 filas), el vector contendrá 5 elementos numerados del
0 al 4.

Punteros y estructuras
Se pueden declarar punteros a estructuras. La forma de hacerlo es igual que para otro tipo
de dato cualquiera de C.
struct nombre_tipo_estructura *nombrepuntero;

Para “apuntar” con un puntero a una variable estructura, se utiliza el operador & (operador
dirección), igual que para otro tipo de dato cualquiera de C.
struct nombre_tipo_estructura nombrevariable;
nombrepuntero = &nombrevariable;

Ahora el puntero nombrepuntero apunta a la variable estructura nombrevariable, y esto nos


permite una nueva forma de acceder a sus miembros, utilizando el operador flecha (->),
llamado operador puntero a miembro de una estructura, formado por los símbolos (-) y (>).

Se utiliza de forma análoga a como se utilizaba el operador punto (.), pero en lugar de utilizar
el nombre de la variable estructura de utiliza el nombre del puntero.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario -8-

nombrepuntero->nombremiembro

También se podría utilizar el operador asterisco (*) pero es muy infrecuente:


(*nombrepuntero).nombremiembro

En este segundo caso, obsérvese la necesidad de utiliza los paréntesis, por la mayor prioridad
del operador (.) respecto al (*).

En el siguiente ejemplo se utiliza el operador puntero a miembro de una estructura dentro


de un array de estructuras.
#include <stdio.h>
#include <stdlib.h>
struct medidas {
int ancho;
int largo;
};
int main(void) {
struct medidas muebles[3]; /* Array de 3 estructuras "medidas" */
struct medidas *m; /* Puntero a estructuras "medidas" */
int i;
m=muebles; /* El puntero "m" apunta al array "muebles" */
for (i=0; i<3; i++) {
muebles[i].ancho=2*(i+1);
muebles[i].largo=3*(i+1);
}

/* >>> Las 4 siguientes sentencias son equivalentes <<< */


/* ============================================ */

for (i=0; i<3; i++)


printf("Medidas: %d x %d\n", muebles[i].ancho, muebles[i].largo);
printf ("\n");
for (i=0; i<3; i++)
printf("Medidas: %d x %d\n", (muebles+i)->ancho,(muebles+i)->largo);
printf ("\n");
for (i=0; i<3; i++)
printf("Medidas: %d x %d\n", m[i].ancho, m[i].largo);
printf ("\n");

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario -9-

for (i=0; i<3; i++)


printf("Medidas: %d x %d\n", (m+i)->ancho, (m+i)->largo);
printf ("\n");

system ("pause");
return 0;
}

Operaciones con estructuras como unidades


A parte de las operaciones que ya hemos visto con variables estructuras:
 acceder individualmente a sus miembros (con . o ->).
 obtener su dirección (con el operador &).
 iniciarla en su declaración.

También es posible realizar ciertas operaciones que actúen sobre toda la estructura como una
unidad:
 asignar una estructura completa a otra, como una unidad1 (siempre que ambas sean del
mismo tipo), con el operador asignación (=).
 pasar una estructura completa como argumento a una función 2 (tanto por valor como por
referencia), y devolver una estructura como valor de retorno de una función.

Sin embargo, no es posible comparar dos estructuras como una unidad; debe hacerse
miembro a miembro.

1
Esta característica se incluye en el C estándar ANSI. En compiladores que no soportan C ANSI no es posible
asignar estructuras como unidades de información, por lo que no se pueden pasar por valor estructuras a
funciones, ni pueden ser devueltas por éstas como valor de retorno. Para ello, en esos compiladores se debe
trabajar con punteros.
2
A la hora de pasar estructuras a funciones se ha de tener en cuenta que si ésta es muy complicada (con muchos
miembros) se escribe y borra mucho en la pila, con la consiguiente pérdida de tiempo. Por eso se aconseja
utilizar punteros en estos casos, es decir, pasarlas por referencia aunque no se vayan a modificar dentro de la
función.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 10 -

Campos de bits
En algunas aplicaciones puede desearse trabajar con elementos que consten de unos pocos
bits (por ejemplo un indicador de un bit para una condición verdadera/falsa), un entero de 3
bits cuyo rango vaya de 0 a 7, un carácter ASCII de 7 bits). Varios datos de este tipo se
pueden empaquetar en una sola palabra de memoria. Para hacer esto la palabra se subdivide
en campos de bits individuales.
Estos campos de bits se definen como miembros de una estructura, de forma que nos permite
acceder a los bits contenidos en un byte o en una palabra de forma individual, como cualquier
otro miembro de una estructura.

Un campo de bits no es más que un tipo especial de miembro de una estructura que define la
longitud en bits que debe tener cada elemento.

En términos generales la descomposición de una palabra de memoria en distintos campos de


bits puede escribirse como
struct nombre_tipo_estructura {
tipo1 miembro_campo_bits1:longitud1;
tipo2 miembro_campo_bits2:longitud2;
...
tipon miembro_campo_bitsn:longitudn;
};

donde los miembros individuales (los campos de bits) tienen el mismo significado que en una
declaración de estructura, pero ahora, cada declaración de miembro debe incluir una
especificación del tamaño en bits del campo correspondiente. Para hacerlo, el nombre del
miembro debe ir seguido por dos puntos (:) y un entero sin signo que indique el tamaño del
campo.

Un campo de bits debe ser declarado como int o unsigned int. Los campos de bits de
longitud 1 deben ser declarados unsigned porque un único bit no puede tener signo.

Normalmente los programas que incluyen campos de bits no son directamente transportables
entre máquinas. Esto es debido a que la asignación de estos campos de bits puede variar de un
compilador a otro. Por ejemplo, algunos compiladores pueden asignar los campos de bits de
derecha a izquierda, mientras que otros los asignan de izquierda a derecha (deberá
consultarse, por tanto, el manual del compilador correspondiente). Supondremos asignación
de derecha a izquierda en los ejemplos que se muestren en este tema.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 11 -

Por ejemplo, consideremos un programa en C contiene las siguientes declaraciones


struct muestra {
unsigned a : 1;
unsigned b : 3;
unsigned c : 2;
unsigned d : 1;
};
struct muestra v;

La primera declaración define una estructura que se subdivide en cuatro campos de


bits, llamados a, b, c y d. Estos campos tienen tamaños de 1 bit, 3 bits, 2 bits y 1 bit
respectivamente. Así, los campos de bits ocupan un total de 7 bits dentro de una
palabra en memoria. El resto de los bits dentro de la palabra quedarán sin usar.

La figura ilustra un boceto de los campos de bits dentro de la palabra, suponiendo una
palabra de 16 bits y asignación de derecha a izquierda.
bit número
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

sin uso
d c b a

Figura 2. Campos de bits dentro de una palabra de 16 bits

La segunda declaración establece que v es una variable de estructura tipo muestra. Así,
v.a es un campo dentro de v de tamaño 1 bit. Análogamente v.b es un campo de 3 bits, y
así sucesivamente.

 No se pueden construir matrices de campos de bits.


 Los campos de bits no tienen direcciones (pues la menor unidad direccionable es el byte),
por lo que no se puede utilizar el operador dirección (&) sobre ellos.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 12 -

Uniones
Las uniones, son similares a las estructuras, ya que contienen miembros cuyos tipos de datos
pueden ser diferentes. La diferencia de las uniones con las estructuras es que los miembros
que componen una unión comparten la misma área de almacenamiento dentro de la memoria,
mientras que cada miembro de una estructura tiene asignada su propia área de
almacenamiento.

 Las uniones se usan para ahorrar memoria. Son útiles para aplicaciones que involucren
múltiples miembros donde no se necesita asignar valores a todos los miembros
simultáneamente.
 Son muy utilizadas cuando se necesita manejar diferentes clases de datos en una única
área de memoria.

La forma de declarar una unión es análoga a la de las estructuras. También la forma de


acceder a los elementos, bien por nombre, bien mediante puntero.

Por ejemplo, supongamos que se desea acceder a un byte de dos formas distintas. Una como
entidad de información (un carácter), y la otra bit a bit. Para ello se declara un campo de bits y
una unión de la siguiente forma:
struct byte {
int a:1;
int b:1;
int c:1;
int d:1;
int e:1;
int f:1;
int g:1;
int h:1;
};
union caracter {
char car;
struct byte bit;
} car_1;

La union caracter almacena los miembros car y bit en el mismo byte en memoria, por lo que
puede accederse a la información de dos formas:
car_1.car; /* acceso al byte como un tipo char normal */
car_1.bit.c; /* acceso al tercer bit del byte */

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 13 -

Cuando en una union sus miembros son de tipos de datos distintos con distinta ocupación en
memoria, el tamaño de la misma será el del mayor de sus miembros.

En el siguiente ejemplo definimos una union formada por un entero, un entero corto, un
entero largo, un real float, una cadena de 6 caracteres, y un real double. La longitud de la
unión será 8 bytes, por ser el double el miembro más largo.

#include <stdio.h>
#include <stdlib.h>
union union6 {
int i;
short si;
long li;
float f;
char s[6];
double df;
};
int main(void) {
union union6 UnaUnion;
printf("OCUPACION EN MEMORIA DE LAS UNIONES\n");
printf("Tamaño miembro int (i) es : %2d\n", sizeof(UnaUnion.i));
printf("Tamaño miembro short (si) es : %2d\n", sizeof(UnaUnion.si));
printf("Tamaño miembro long (li) es : %2d\n", sizeof(UnaUnion.li));
printf("Tamaño miembro float (f) es : %2d\n", sizeof(UnaUnion.f));
printf("Tamaño miembro cadena (s) es : %2d\n", sizeof(UnaUnion.s));
printf("Tamaño miembro double (df) es : %2d\n", sizeof(UnaUnion.df));
printf("Tamaño de la unión UnaUnion es: %2d\n", sizeof(UnaUnion));
printf ("\n");
system ("pause");
return 0;
}

Ejemplo de ejecución del programa


OCUPACION EN MEMORIA DE LAS UNIONES
Tamaño miembro int (i) es : 4
Tamaño miembro short (si) es : 2
Tamaño miembro long (li) es : 4
Tamaño miembro float (f) es : 4
Tamaño miembro cadena (s) es : 6
Tamaño miembro double (df) es : 8
Tamaño de la unión UnaUnion es: 8

Ejemplo práctico de utilización de un campo de bits

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 14 -

Escribiremos un programa que pida una línea de texto por teclado y visualice en binario,
según el código ASCII, los caracteres de la misma.
Se utilizará una unión que contenga un carácter y una estructura de campos de bits para
contener el octeto correspondiente a cada carácter de la línea de texto.

/* Fuente: CAMPOBITS.C
Programa USO DE LOS CAMPOS DE BITS
Descripción: Presenta cada carácter de una cadena en ASCII y binario
Para la conversión a binario utiliza un campo de bits
*/
#include <stdio.h>
#include <stdlib.h>

struct octeto {
int a : 1;
int b : 1;
int c : 1;
int d : 1;
int e : 1;
int f : 1;
int g : 1;
int h : 1;
};

union bits {
char car;
struct octeto bit;
};

/**----- Prototipos -----**/


void visualizabit (union bits);

int main (void) {


char frase[81];
int i;
union bits codigo;

printf("Introduzca una frase (máx. 80 caracteres): ");


gets(frase);

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 15 -

i = 0;
printf("Carácter ASCII binario\n");
printf("-------- ----- -------\n");
while (frase[i] != '\0') {
codigo.car = frase[i];
printf("%4c %8d ", codigo.car, codigo.car);
visualizabit(codigo);
printf("\n");
i++;
}

printf ("\n");
system ("pause");
return 0;
}

void visualizabit (union bits x) {


if (x.bit.h) printf("1"); else printf("0");
if (x.bit.g) printf("1"); else printf("0");
if (x.bit.f) printf("1"); else printf("0");
if (x.bit.e) printf("1"); else printf("0");
if (x.bit.d) printf("1"); else printf("0");
if (x.bit.c) printf("1"); else printf("0");
if (x.bit.b) printf("1"); else printf("0");
if (x.bit.a) printf("1"); else printf("0");
}

Obsérvese que el bit h es el más significativo, el más a la izquierda de los 8 bits.

8 7 6 5 4 3 2 1
h g f e d c b a

Figura 3. Distribución de bits dentro de un byte

Sin embargo en la estructura octeto se ha definido el último. Esto es debido a que el


compilador cuando define las variables en memoria, las coloca en direcciones contiguas
correspondiendo la dirección más baja la de la primera escrita en el programa. Y en este caso
el campo de bits octeto está siendo tratado como un conjunto de 8 variables independientes.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 16 -

Ejemplo de ejecución del programa


Introduzca una frase (máx. 80 caracteres): ABCD1235
Carácter ASCII binario
-------- ----- --------
A 65 01000001
B 66 01000010
C 67 01000011
D 68 01000100
1 49 00110001
2 50 00110010
3 51 00110011
5 53 00110101

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 17 -

Enumeraciones
Una enumeración es un tipo de dato que consta de una lista de constantes enteras con signo.
Los nombres de estas constantes representan los valores legales que puede tomar una
variable que se defina de ese tipo.

La declaración de la plantilla o tipo de una enumeración se realiza con la palabra reservada


enum y la lista de constantes3 encerrada entre paréntesis.
enum nombre_tipo_enumeracion {
constante1,
constante2,
...
constanten,
};

Los nombres de las constantes deben ser todos diferentes y distintos a los nombres de otros
identificadores cuyo ámbito sea el mismo que el de la enumeración.

La definición de variables tipo enum se realiza de forma análoga a como se define una
variable estructura:
enum nombre_tipo_enumeracion variable_enumeracion;
o bien
enum nombre_tipo_enumeracion {
constante1,
constante2,
...
constanten,
} variable_enumeracion;
o bien
enum {
constante1,
constante2,
...
constanten,
} variable_enumeracion;

Internamente los elementos que forman parte de una enumeración son constantes enteras con
signo cuyos valores son asignados automáticamente por el compilador, comenzando por cero
para la primera constante, uno para la segunda, y así sucesivamente.

3
También se les suele llamar miembros de la enumeración a cada una de las constantes de la lista

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 18 -

Por ejemplo, puede pensarse en una variable llamada diasemana que sólo puede tomar los 7
siguientes valores: lunes, martes, miercoles, jueves, viernes, sabado y domingo.

Comenzaremos por declarar un nuevo tipo enumeración al que llamaremos dia.


enum dia {lunes, martes, miercoles, jueves, viernes, sabado, domingo };

La declaración anterior crea un nuevo tipo de dato (tipo enum dia) cuyas variables que se
definan de dicho tipo sólo podrán tomar uno de los 7 valores especificados.

Posteriormente la definición de la variable diasemana se realizará de la siguiente forma:


enum dia diasemana;

Internamente los elementos que forman parte de una enumeración son constantes tipo int con
los siguientes valores:
lunes 0
martes 1
miercoles 2
jueves 3
viernes 4
sabado 5
domingo 6

Al declarar un tipo enumerado, se puede redefinir los valores que el compilador asigna
automáticamente, iniciando las constantes con los valores que se deseen, recordando que cada
constante no iniciada toma un valor superior en una unidad a la anterior.
enum monedas {peseta=1, duro=5, cincoduros=25, veinteduros=100};
enum mobiliario {silla, mesa, puerta=100, escalera, armario};

En esta segunda declaración los valores que tomarán las constantes son
silla 0
mesa 1
puerta 100
escalera 101
armario 102

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 19 -

Referencia a las variables enumeración


Para dar valor a una variable enumeración se utiliza el nombre de la constante
correspondiente.
Por ejemplo, supongamos la definición de la variable enumerada color.
enum colores {rojo, amarillo=4, verde, azul};
enum colores color;

Los únicos operadores que pueden acompañar a los tipos enumerados son el operador de
asignación y los de relación.

 Asignación de valor: color = verde;

 Comparación para decisión: if (color == amarillo)


....

No es posible leer (teclado, fichero, ...) una variable enumerada y asignarle un valor. Podría
leerse un entero y asignárselo a la variable enumerada, aunque esto no es muy común.

Tampoco es posible escribir (pantalla, fichero, ...) una variable enumerada. Sólo el valor
entero de una variable enumerada podría ser mostrado.

El siguiente ejemplo presenta una posible forma de dar valor a una variable enumeración a
partir de la lectura desde teclado de un número entero.
Además, ilustra como poder utilizar una variable enumerada como variable de control de un
for y como subíndice para un array, es decir, utilizando la característica de que internamente
los elementos que forman parte de una enumeración son constantes enteras con signo cuyos
valores son asignados automáticamente por el compilador, comenzando por cero, en caso de
no especificar explícitamente otro valor para las mismas.

#include <stdio.h>
#include <stdlib.h>
enum dias {lunes, martes, miercoles, jueves, viernes, sabado, domingo};

char nombre[][12] = {"Lunes", "Martes", "Miércoles", "Jueves",


"Viernes", "Sabado", "Domingo"};
int main(void) {
enum dias hoy;
int unDia;

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 20 -

printf("Los dias de la semana son:\n");


for (hoy = lunes; hoy <= domingo; hoy++)
printf("El dia %d es el %s\n", hoy, nombre[hoy]);
printf("\n\nEscriba el numero de un día: ");
scanf("%d",&unDia);
printf("El dia %d es el %s\n", unDia, nombre[unDia]);

printf ("\n");
system ("pause");
return 0;
}

Un resultado interesante, que se obtiene en algunos compiladores en el programa, es si damos


el 7 como día de la semana solicitado por teclado. En ese caso el programa indica como
resultado
El dia 7 es el Los dias de la semana son:

¿Qué explicación tiene?. C forma una tabla de cadenas en el lugar oportuno de memoria. Las
cadenas que se han utilizado en este programa son los 7 días de la semana en la cadena
nombre (índices del 0 al 6), y luego la cadena del printf() inicial, que es la que se muestra
para la entrada anterior.

A menudo se puede incrementar la claridad lógica de un programa utilizando variables


enumeradas. Las variables de enumeración son particularmente útiles como indicadores,
para indicar varias opciones para realizar un cálculo, o para identificar condiciones que
puedan haber surgido como resultado de cálculos internos anteriores. Desde esta perspectiva
se aconseja el uso de variables de enumeración dentro de un programa complejo. No
obstante debe quedar claro que se pueden usar variables enteras en lugar de variables de
enumeración. Por lo tanto, las variables de enumeración, a parte de facilitar la codificación
y la legibilidad de los programas, no proporcionan fundamentalmente nuevas capacidades.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 21 -

typedef
Permite que el programador defina nuevos nombres o alias para los tipos ya existentes. No
se trata, por tanto, de la creación de nuevos tipos de datos.

La forma general de la sentencia es: typedef tipo nombre;

Donde tipo es cualquier tipo de datos legal, y nombre el alias. El nuevo nombre es una
adición a los tipos de existentes, no una sustitución de éstos.

Por ejemplo, si se define un alias para el tipo de dato float.


typedef float real;

Con esta definición no se ha creado un tipo nuevo sino que se emplea otro nombre para un
tipo de dato ya existente que sigue estando activo. Por ello, podemos usar real para cualquier
finalidad que pudiéramos hacer con float. A diferencia de otros lenguajes, el nuevo tipo
declarado es completamente compatible con el tipo con el que se declara, es decir, se podrán
intercambiar variables, valores, parámetros, etc., sin ningún problema.
float a = 10.5;
real e = -2073;
e = a;
a = e;

Aunque este último aspecto sugiere una pregunta: ¿para qué queremos crear un tipo nuevo si
lo vamos a seguir mezclando con el viejo?. Como ahora veremos, la utilidad es precisamente
realizar un tipo nuevo que se pueda utilizar independientemente de que cambie su definición.

Por ejemplo, si hemos realizado la definición typedef float real; más adelante podremos
decidir aumentar la precisión del tipo real cambiando la definición del typedef. Gracias a esto
podemos hacerlo sin necesidad de tocar todas las variables y funciones que hayamos definido
a partir del tipo real. Por tanto, es recomendable no mezclar tipos distintos por muy
compatibles que sean.

Por ejemplo, las siguientes definiciones


typedef unsigned char byte;
typedef unsigned short int word;
typedef unsigned long int dword;

son muy usuales para utilizar estos nuevos tipos de manera independiente del compilador y
procesador. Así si se cambia a una nueva máquina sólo habrá que cambiar el typedef
apropiado en una sola parte del código, sin tocar el resto.
Según Kernighan y Ritchie: "Hay dos razones principales para usar declaraciones typedef.
La primera es parametrizar un programa contra problemas de portabilidad. Si los typedef se
usan para tipos de datos que son independientes de la máquina, sólo los typedef deben ser

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 22 -

cambiados cuando el programa se mueva. Una situación común es usar nombres typedef
para diversas cantidades enteras, luego se hace un conjunto apropiado de elecciones de
short, int y long para cada máquina. El segundo propósito de typedef es proveer mejor
documentación para un programa".

Es posible utilizar distintos nombres para un mismo tipo de dato. Por ejemplo,
typedef long entero_largo;
typedef long entero_grande;

De esta forma en el programa se podrá utilizar para definir una variable de tipo long int
llamada longitud, cualquiera de las siguientes definiciones:
long longitud;
entero_largo longitud;
entero_grande longitud;

También se puede usar typedef para los tipos de datos complejos y compuestos:
typedef struct tipo_usuario {
char nombre [40];
char login [16];
int id;
} usuario;

usuario lista [200];

La palabra usuario no es en este caso una variable, sino el alias definido para el nuevo tipo de
dato estructura struct tipo_usuario.

Si no se utilizara typedef deberá definirse la variable lista de la siguiente forma:


struct tipo_usuario lista [200];

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 23 -

Ejercicios resueltos

EJERCICIO 1
Encuentra los errores en la siguiente declaración de estructura y posterior definición de
variable
struct hormiga {
int patas;
char especie[41];
float tiempo;
};
hormiga colonia[200];

Solución
Es necesario conservar la palabra struct en la declaración de variables
struct hormiga colonia[200];

a no ser que se añada una sentencia typedef como la siguiente:


typedef struct hormiga hormiga;

EJERCICIO 2
Declara una variable enum para representar las estaciones del año. Deberán representarse
internamente por las constantes 1, 2, 3 y 4.
Solución
enum estaciones {PRIMAVERA=1, VERANO=2, OTOÑO=3, INVIERNO=4};

EJERCICIO 3
¿Es válida en un programa la siguiente definición?. ¿Por qué?
enum vehiculo {moto, coche, carruaje, autobus, bicicleta, motocicleta};
enum transporte {avion, barco, coche, ferrocarril, tranvia};

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 24 -

Solución
No es válida.
En la definición de una enumeración, los nombres de las constantes deben ser todos diferentes
y distintos a los nombres de otros identificadores cuyo ámbito sea el mismo que el de la
enumeración.
En este caso se producirá un error de compilación porque se detectará una doble declaración
del identificador “coche”.

EJERCICIO 4
Escribir una función que devuelva en un tipo enum estaciones, declarado anteriormente, la
estación del año que se ha introducido por teclado.
Solución
El tipo enumerado asocia constantes enteras a los nombres simbólicos, pero estos nombres
simbólicos no pueden ser leídos de teclado desde una función estándar como scanf() o gets().
Por consiguiente lo que se podría hacer es leer un valor entero que asociar con la constante
simbólica, según la definición del tipo enumerado.

enum estaciones leerEstacion ()


{
int e;
gets("Introduzca un número para la estación del año");
gets(" 1 - Primavera");
gets(" 2 – Verano ");
gets(" 3 – Otoño ");
gets(" 4 – Invierno ");

do
{ printf("==> ");
scanf("%d", &e);
} while (e > 4 || e < 1);

swich (e)
{
case 1: return (PRIMAVERA); break;
case 2: return (VERANO); break;
case 3: return (OTOÑO); break;
case 4: return (INVIERNO); break;
}
}

Hay que destacar que para que la función compile correctamente, es necesario que el tipo
enumerado enum estaciones haya sido declarado a nivel global en el programa.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 25 -

EJERCICIO 5
¿Con typedef se declaran nuevos tipos de datos, o bien permite cambiar el nombre de tipos de
datos ya declarados?.
Solución
La sentencia no añade ningún tipo de dato nuevo a los ya definidos en el lenguaje C.
Simplemente permite renombrar un tipo ya existente, incluso aunque sea un nuevo nombre
para un tipo de dato básico del lenguaje como int o float.

EJERCICIO 6

Se presenta como recibir una estructura como parámetro por valor y por referencia, y como devolverla como
parámetro por referencia o como valor de retorno de la función

A) Declarar un tipo de dato estructura para representar un alumno. Los campos de los que
debe de disponer son:
 Nombre completo, cadena de caracteres
 Curso, entero
 Fecha nacimiento, estructura de tres campos enteros para el día, mes y año
 Dirección completa, cadena de caracteres
 10 notas parciales, vector de 10 números reales
Definir una variable del tipo estructura anterior.

Definiciones en C
#define MAXN 10
struct fecha {
int dia;
int mes;
int anio;
};
struct alumno {
char nombre[51];
int curso;
struct fecha fnac;
char direccion[61];
float notas[MAXN];
};
struct alumno alum;

B) Escribir una función que lea de teclado los campos de una estructura de tipo alumno y la
devuelva al módulo llamador como valor de retorno de la función.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 26 -

Función codificada en C
Como ninguna función intrínseca de entrada por teclado permite leer en una única sentencia
toda la estructura, la función ha de leer por separado cada uno de sus campos, accediendo a
ellos mediante el operador punto. Algunos compiladores no permiten devolver una estructura
como valor de retorno de una función, por lo que en estos casos haría que utilizar el paso por
referencia, como se verá más adelante.

struct alumno leerAlumno() {


struct alumno aa;
int i;

printf("Introduzca el nombre: ");


fflush(stdin);
gets(aa.nombre);

printf("Introduzca el curso: ");


scanf("%d", &aa.curso);

printf("Introduzca la fecha de nacimiento:\n");


printf("Introduzca el día: ");
scanf("%d", &aa.fnac.dia);

printf("Introduzca el mes: ");


scanf("%d", &aa.fnac.mes);

printf("Introduzca el año: ");


scanf("%d", &aa.fnac.anio);

printf("Introduzca la dirección: ");


fflush(stdin);
gets(aa.direccion);

for (i=0; i<MAXN; i++)


{ printf("Introduzca la nota %2d: ", i+1);
scanf("%f", &aa.notas[i]);
}

return aa;
}

El prototipo de la función es struct alumno leerAlumno(void);


y la forma de llamarla alum = leerAlumno();
C) Escribir una función que lea de teclado los campos de una estructura de tipo alumno y la
devuelva al módulo llamador a través de un parámetro que se le haya pasado por
referencia.

Función codificada en C

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 27 -

Al pasar una estructura por referencia, por medio de un puntero a la misma, hay que utilizar el
operador flecha (->) para acceder a cada uno de sus campos. En este caso la estructura no es
una variable local, sino que se están utilizando las posiciones de memoria originales del
parámetro real del módulo llamador.

void leerAlumno(struct alumno *aa) {


int i;

printf("Introduzca el nombre: ");


fflush(stdin);
gets(aa->nombre);

printf("Introduzca el curso: ");


scanf("%d", &aa->curso);

printf("Introduzca la fecha de nacimiento:\n");


printf("Introduzca el día: ");
scanf("%d", &aa->fnac.dia);

printf("Introduzca el mes: ");


scanf("%d", &aa->fnac.mes);

printf("Introduzca el año: ");


scanf("%d", &aa->fnac.anio);

printf("Introduzca la dirección: ");


fflush(stdin);
gets(aa->direccion);

for (i=0; i<MAXN; i++)


{ printf("Introduzca la nota %2d: ", i+1);
scanf("%f", &aa->notas[i]);
}
}

El prototipo de la función es void leerAlumno(struct alumno *);


y la forma de llamarla leerAlumno(&alum);

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 28 -

D) Escribir una función que reciba una estructura de tipo alumno por valor, y presente sus
campos en pantalla.

Función codificada en C
Como ninguna función intrínseca de salida por pantalla permite mostrar en una única
sentencia toda la estructura en pantalla, utilizaremos la función printf para mostrar uno a uno
los campos de la misma.
void mostrarAlumno(struct alumno aa) {
int i;
printf("Nombre: %s\n", aa.nombre);
printf("Curso : %d\n", aa.curso);
printf("Fecha de nacimiento: %d/%02d/%d\n",
aa.fnac.dia, aa.fnac.mes, aa.fnac.anio);
printf("Dirección: %s\n", aa.direccion);
for (i=0; i<MAXN; i++)
printf("Nota %2d: %5.2f\n", i+1, aa.notas[i]);
}

El prototipo de la función es void mostrarAlumno(struct alumno);


y la forma de llamarla mostarAlumno(alum);

E) Escribir una función que reciba una estructura de tipo alumno por referencia, y presente
sus campos en pantalla.
Función codificada en C
void mostrarAlumno(struct alumno *aa) {
int i;
printf("Nombre: %s\n", aa->nombre);
printf("Curso : %d\n", aa->curso);
printf("Fecha de nacimiento: %d/%02d/%d\n",
aa->fnac.dia, aa->fnac.mes, aa->fnac.anio);
printf("Dirección: %s\n", aa->direccion);
for (i=0; i<MAXN; i++)
printf("Nota %2d: %5.2f\n", i+1, aa->notas[i]);
}

El prototipo de la función es void mostrarAlumno(struct alumno *);


y la forma de llamarla mostarAlumno(&alum);

Hay que destacar que en todas las funciones anteriores, para que la compilen correctamente,
es necesario que el tipo struct alumno haya sido declarado a nivel global en el programa.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 29 -

EJERCICIO 7

Escribir tres funciones que permitan hacer las operaciones de suma, resta y multiplicación de
números complejos. El tipo complejo deberá definirse como una estructura.
Escribir además una función para leer un complejo de teclado y otra para mostrarlos en
pantalla.
Escribir finalmente un programa para comprobar el correcto funcionamiento de las funciones.

Análisis del problema


Un número complejo está formado por dos números reales, uno de ellos denominado parte
real y el otro parte imaginaria. La forma normal de representar en matemáticas un número
complejo s la siguiente: real + i * imaginaria, donde el símbolo i se denomina unidad
imaginaria y simboliza la raíz cuadrada de –1.
Debido a su naturaleza compuesta, un número complejo se puede representar de forma natural
por una estructura de dos campos de tipo real que contendrán la parte real y la imaginaria
respectivamente. Las funciones que siguen traducen las operaciones matemáticas tal y como
se definen en la aritmética de números complejos.

Funciones codificadas en C
struct complejo {
float real;
float imag;
};

struct complejo sumaComplejo (struct complejo a, struct complejo b)


{
struct complejo c;
c.real = a.real + b.real;
c.imag = a.imag + b.imag;
return c;
};

struct complejo restaComplejo (struct complejo a, struct complejo b)


{
struct complejo c;
c.real = a.real - b.real;
c.imag = a.imag - b.imag;
return c;
};

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 30 -

struct complejo multiplicaComplejo (struct complejo a, struct complejo b)


{
struct complejo c;
c.real = a.real * b.real – a.imag * b.imag;
c.imag = a.real * b.imag + a.imag * b.real;
return c;
};

void leerComplejo (struct complejo *a)


{
struct complejo c;
printf("Introduzca la parte real : ");
scanf("%f", &c.real);
printf("Introduzca la parte imaginaria: ");
scanf("%f", &c.imag);
*a = c;
};

void mostrarComplejo (struct complejo a)


{
printf("%.2f", a.real);
if (a.imag < 0)
printf(" - %.2fi", -a.imag);
else printf(" + %.2fi", a.imag);
};

Hay que destacar que en todas las funciones anteriores, para que la compilen correctamente,
es necesario que el tipo struct complejo haya sido declarado a nivel global en el programa.

Programa completo codificado en C

Ver fuente COMPLEJO.C

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 31 -

EJERCICIO 8
Se desea calcular la edad y estatura máxima, mínima y media de la alineación inicial de un
equipo de fútbol.
Para ello se realizará un programa que pida por teclado el nombre, la edad y la estatura de los
11 jugadores. Una vez introducidos los datos, el programa presentará en pantalla:
− La estatura y edad media del equipo.
− La edad mínima del equipo y el número de jugadores con su nombre y estatura que la
poseen.
− La estatura máxima del equipo y el número de jugadores con su nombre y edad que la
poseen.
Análisis
Se definirá una estructura llamada jugador, que contendrá los datos que recoge el programa
de cada jugador del equipo: su nombre, su edad y su estatura. Posteriormente se definirá un
vector, llamado equipo, de elementos de esta estructura. La dimensión de este vector se
definirá en función de una constante DIM, cuyo valor se fija en 11 (según el enunciado del
problema).
#define DIM 11
struct jugador {
char nombre [81];
int edad;
float talla;
};
struct jugador equipo[DIM];

equipo[0].nombre
equipo[0] equipo[0].edad
equipo[0].talla
equipo[1].nombre
equipo[1] equipo[1].edad
equipo[1].talla

equipo[DIM-1].nombre
equipo[DIM-1] equipo[DIM-1].edad
equipo[DIM-1].talla

Figura 4. Representación
gráfica del vector de estructuras equipo y sus miembros

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 32 -

Programa codificado en C

Ver fuente EQUIPO.C

Observaciones al programa anterior:


- strlen() es la función que calcula la longitud de una cadena de caracteres. Su archivo de
cabecera es string.h
- la sentencia for (i=1; i<=strlen(nombre_equipo); i++) printf("=");
escribe una línea de sobre subrayado (=) de la misma longitud que la cadena de
caracteres nombre_equipo.
- el especificador de formato printf ("%-30s %7d\n", nombre[i], edad[i])
permite mostrar en pantalla la cadena de caracteres nombre[i] en 30 posiciones y
justificada a la izquierda.

EJERCICIO 9

Un formato de coma flotante para la representación de números reales es el estándar


IEEE 754, propuesto por el Institute of Electrical and Electronics Engineers.

Según indica este estándar, el formato de representación de números en 32 bits está


compuesto por el signo de la mantisa, el exponente y la mantisa, según se describe en la
siguiente tabla.

Formato IEEE 754 coma flotante en 32 bits


Signo mantisa (S) 1 bit (posición 31)
Exponente o característica (E) 8 bits (posiciones 23 a 30)
Mantisa (m) 23 bits (posiciones 0 a 22)
Tabla 1. Formato IEEE 754 para números reales en coma flotante en 32 bits

La distribución de bits anterior queda dibujada en la siguiente figura

S E m

31 23 0
Número de bit
Figura 5. Distribución de bits en el formato IEEE 754
para números reales en coma flotante en 32 bits

En particular, el almacenamiento en C de un número float utiliza este estándar.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 33 -

Realizar un programa que permita comprobar la estructura binaria de los datos tipo float.
Utilizar para ello una unión con dos miembros, un float y un campo de bits de 32 bits.

Programa codificado en C
Ver fuente BITFLOAT.C

Observaciones al programa anterior


Obsérvese que el bit0 es el menos significativo, el más a la derecha de los 4 bytes.

31 30 29 28 27 26 25 24 12 ... 7 6 5 4 3 2 1 0
Número de bit

Figura 6. Campo de bits para manejar un dato de 32 bits

Sin embargo en la estructura TreintaydosBits se ha definido el primero. Esto es debido a que


el compilador cuando define las variables en memoria, las coloca en direcciones contiguas
correspondiendo la dirección más baja la de la primera escrita en el programa. Y en este caso
el campo de bits TreintaydosBits está siendo tratado como un conjunto de 32 variables
independientes.

Ejemplo de ejecución

FORMATO COMA FLOTANTE IEEE 754 PARA NUMEROS FLOAT


=================================================
Presenta la estructura binaria de los datos tipo float, indicando el bit
del signo de la mantisa (S) y los bits del exponente (E) y la mantisa (m)

Número S Exponente Mantisa


------- - --------- -----------------------
-10.00 = 1 * 10000010 * 01000000000000000000000
-9.00 = 1 * 10000010 * 00100000000000000000000
-8.00 = 1 * 10000010 * 00000000000000000000000
-7.00 = 1 * 10000001 * 11000000000000000000000
-6.00 = 1 * 10000001 * 10000000000000000000000
-5.00 = 1 * 10000001 * 01000000000000000000000
-4.00 = 1 * 10000001 * 00000000000000000000000
-3.00 = 1 * 10000000 * 10000000000000000000000
-2.00 = 1 * 10000000 * 00000000000000000000000
-1.00 = 1 * 01111111 * 00000000000000000000000
0.00 = 0 * 00000000 * 00000000000000000000000
1.00 = 0 * 01111111 * 00000000000000000000000
2.00 = 0 * 10000000 * 00000000000000000000000
3.00 = 0 * 10000000 * 10000000000000000000000
4.00 = 0 * 10000001 * 00000000000000000000000
5.00 = 0 * 10000001 * 01000000000000000000000
6.00 = 0 * 10000001 * 10000000000000000000000
7.00 = 0 * 10000001 * 11000000000000000000000

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 34 -

8.00 = 0 * 10000010 * 00000000000000000000000


9.00 = 0 * 10000010 * 00100000000000000000000
10.00 = 0 * 10000010 * 01000000000000000000000

Introduzca un número float: 234.45


234.45 = 0 * 10000110 * 11010100111001100110011
¿Otra ejecución (S/N)?: S

Introduzca un n·mero float: -2324.56789


-2324.57 = 1 * 10001010 * 00100010100100100010110
¿Otra ejecución (S/N)?: N

Al final de este mismo documento puede encontrarse más información sobre el estándar
IEEE 754.

En el fuente BITFLOA2.C se presenta una modificación al anterior programa para ilustrar


que el estudio de la estructura binaria del dato float podría haberse realizado utilizando
únicamente una estructura campo de bits.
En ese caso la dificultad es asignar el valor float a investigar a la estructura. Como esto no se
permite realizar de forma directa, se utiliza un puntero.
Es decir, si definimos
struct TreintaydosBits {
unsigned bit0 : 1 ;
unsigned bit1 : 1 ;
...
unsigned bit30 : 1 ;
unsigned bit31 : 1 ;
} bits;

float unreal;

es ilegal asignar bits = unreal;

Pero puede hacerse a través de un puntero de la forma:


float *ptroreal;

ptroreal = (float *) &bits; /* El puntero apunta a "bits" */


*ptroreal = unreal; /* A través del puntero asignamos "unreal" a "bits" */

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 35 -

Programas complementarios

Se presentan a continuación varios programas ejemplo que permiten comprobar el


comportamiento de los tipos definidos por el programador en C.

Nombre Descripción / Comentarios


POTENCIA.C Programa: Base elevada a una potencia.
Descripción: Ilustra como se puede usar una unión para pasar
información a una función. En el programa se resuelve la elevación de un
número a una potencia: y = x^n
x e y son valores en coma flotante y n puede ser un número entero o en
coma flotante.
 Si n es entero, entonces y puede ser evaluado multiplicando x por sí
misma un número n de veces.
 Si n es un valor en coma flotante, entonces para calcular la potencia y
podemos utilizar la expresión siguiente
y = x^n ===> log y = n log x ===> y = e ^(n log x)
donde x debe ser una cantidad positiva..

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 36 -

Ejercicios propuestos

EJERCICIO 10
Un punto en el plano se puede representar mediante una estructura con dos campos. Escribir
un programa que mediante funciones que manejen estructuras que representen los puntos del
plano realice las siguientes operaciones.
 Leer un punto de teclado
 Presentar un punto en pantalla
 Dados dos puntos, calcular la distancia entre ellos
 Dados dos puntos, determinar el punto medio de la línea que los une.

Análisis del problema


Las coordenadas de un punto en el plano de dos dimensiones son dos números reales que se
representarán en forma de estructura. Es decir, por cada punto se define una estructura.
La distancia entre dos puntos se calcula como la raíz cuadrada de la suma del cuadrado de la
diferencia de sus coordenadas, es decir sqrt(pow(p1.x-p2.x, 2) + pow(p1.y-p2.y, 2))
El punto medio de la línea que los une se calcula
p3.x = (p1.x + p2.x) / 2
p3.y = (p1.y + p2.y) / 2

EJERCICIO 11
Realizar un programa que determine la estructura binaria de los datos tipo long.
Utilizar una unión con dos miembros, un long y un campo de bits de 32 bits.

EJERCICIO 12
Escribir un programa para realizar aritmética fraccional. El usuario introduce una fracción
(numerador y denominador), una operación ( +, -, / ó * ) y otra fracción (numerador y
denominador); por ejemplo:
1 15 / 1 3
Debería dar lugar al resultado 3/15 (no 0.2).
Utilizar estructuras para trabajar con los números fraccionarios (numerador y denominador).

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 37 -

EJERCICIO 13

Ampliar el ejercicio anterior para que reduzca la fracción a su forma más simplificada. En el
ejemplo anterior, debe darse como resultado 1/5.

Recordar que para obtener la forma más simplificada de una fracción deberemos calcular el
máximo común divisor (mcd) del numerador y denominador, y dividir tanto el numerador
como el denominador por el mcd.
Para calcular el mcd puede utilizarse el algoritmo de Euclides.

Dados dos números naturales A, B (A >


B)

Repetir
C  resto división A/B
Si C <> 0 entonces
A  B
B  C
hasta C = 0

El mcd es B
Algoritmo de Euclides

EJERCICIO 14

Diseñar un patrón de estructura, a nivel global, con los miembros adecuados para poder
almacenar
- el nombre de un mes
- una abreviatura de tres letras para el mismo
- el número de días del mes
- el orden de éste en el año

Escribir una función que defina localmente un array de 12 estructuras del tipo anterior,
iniciándolo para un año no bisiesto.

La función deberá recibir el número del mes, y el día dentro del mismo, y deberá devolver el
número total de días transcurridos en el año hasta a ese día.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)
Programación II
Tipos de dato definidos por el usuario - 38 -

EJERCICIO 15

Realizar un programa que permita comprobar la estructura binaria de los datos tipo char
como enteros cortos con signo, codificados en complemento a 2.

Utilizar para ello una unión con dos miembros, un char y un campo de bits de 8 bits.

Grado en Ingeniería Informática – Programación II


Departamento de Informática y Automática – Universidad de Salamanca (v 1112)

You might also like