/* Apuntes STRUCTs en C
por Luis León Cárdenas Graide - lcardena@dcc.uchile.cl
versión 2003/09/15
Por favor, avisar errores u omisiones.
Motivación: Es muy frecuente encontrarse ante situaciones en las que es
necesario manejar datos agrupados lógicamente. Por ejemplo, si se está
trabajando con la información de clientes de una empresa, se necesita manejar
su nombre, edad y dirección en forma conjunta. Sin embargo, la utilización de
variables simples obliga a trabajarlas por separado. Así, las funciones que
operen con clientes necesitarán recibir un parámetro por cada dato del cliente
que se desee administrar. Este enfoque puede resultar bastante incómodo,
sobretodo si se desea trabajar con grandes conjuntos de clientes. Habría que
manejar, por ejemplo, un arreglo para los nombres, otro para las edades, otro
para las direcciones y realizar con cada uno las mismas operaciones. Si bien
esta solución es perfectamente válida, no es para nada cómoda. En ese sentido,
C permite definir y manipular datos definidos por el usuario en los cuales se
aglomeran varios datos más simples, tratándolos a todos juntos como una unidad.
Ése es el propósito de las estructuras en C (struct).
Una estructura se define como sigue:
*/
struct tipo_estructura {
tipo1 nombreCampo1;
...
tipoN nombreCampoN;
}; /* OJO: NO olvidar el ';' final */
/* Esto define una estructura de tipo <tipo_estructura> y campos <nombreCampo1>
a <nombreCampoN>, siendo cada uno de tipo <tipo1> a <tipoN>, respectivamente.
Volviendo al ejemplo del cliente, una definición de un dato estructurado que
lo represente podría ser el siguiente: */
struct cliente {
char nombre[32];
int edad;
char direccion[128];
};
/* Esto define una estructura de nombre <cliente> y con los campos <nombre>,
que es un arreglo de 32 caracteres, <edad>, que es un int, y <direccion>, que
es un arreglo de 128 caracteres. De ahora en adelante, cada variable que se
declare de tipo "struct cliente" contendrá estos tres campos. Una de las
gracias de este enfoque es que ahora los identificadores "nombre", "edad" y
"direccion" son sólo visibles dentro de la estructura y no invaden el espacio
de nombres que se utilizan para las demás variables. Con esto, se consigue
evitar saturar el espacio de nombres, cosa que ocurre fácilmente al programar
en C.
Una vez declarada la estructura, las variables que se deseen declarar del tipo
de la estructuras se crean de la siguiente forma: */
{/* Algún bloque */
struct cliente c;
/* Ahora <c> ES una ENCARNACIÓN de la estructura. A sus campos se podrá
acceder intercalando el operador '.' entre el nombre de la variable y el del
campo.
Nota: La variable <c> ahora es una variable compleja, que está compuesta
por otros datos más básicos, lo que la hace más "gorda", pero desde el punto
de vista de se runa variable, es tan variable como cualquier otra. En este
caso, <c> vive en el stack y será reciclada al terminar el bloque en el que
fue creada.
OJO: Como siempre, inicialmente, los campos de <c> contienen basura, así
que hay que inicializarlos: */
strcpy(c.nombre, "Pepe");
c.edad = 20;
strcpy(c.direccion, "Alameda");
}
/* Nótese que los nombres de los campos no interfieren con los nombres de otras
variables, así que, perfectamente, podría haberse creado la estructura de la
siguiente forma: */
{
struct cliente c;
char nombre[32] = "Andrés";
int edad = 15;
char direccion[128] = "Gran Avenida";
strcpy(c.nombre, nombre);
c.edad = edad;
strcpy(c.direccion, direccion);
}
/* Nótese que así como el tipo de <edad> es <int>, el tipo de <c> es <struct
cliente> y no <cliente>. Salvo ese detalle, que hace que su declaración sea un
tanto más verbosa, su comportamiento es idéntico al de cualquier otra variable.
De hecho, puede haber punteros a estructuras: */
{
struct cliente c, *c_p;
/* Equivale a:
struct cliente c;
struct cliente *c_p;
Como siempre, los punteros contienen basura en un comienzo, así que, antes
de utilizarlo, hay que inicializarlo debidamente: */
c_p = &c; /* Ahora <c_p> contiene la dirección de la ubicación de <c>. */
/* Pero la estructura también contiene basura, así que hay que también hay
que inicializarla; esta vez, hagámoslo con el puntero: */
strcpy((*c_p).nombre, "Víctor");
(*c_p).edad = 77;
strcpy((*c_p).direccion, "Las Condes");
/* Así como <c_p> es un puntero a la estructura, <*c_p> ES la estructura, así
que se puede acceder a sus campos mediante '.'. Los paréntesis son necesarios
porque el operador de acceso a miembros '.' tiene mayor precedencia que el
operador de desreferenciación '*'.
Por una cuestión de comodidad, como puede ser engorroso utilizar paréntesis
y '*' cada vez que se tiene un puntero a una estructura, C proporciona el
operador de acceso a miembros de una estructura desde un puntero: "->". Así,
se puede acceder a sus miembros en forma directa desde el puntero. De este
modo, las tres líneas anteriores pueden reescribirse así: */
strcpy(c_p->nombre, "Víctor");
c_p->edad = 77;
strcpy(c_p->direccion, "Las Condes");
/* Esta es una mera abreviación sintáctica. Al final, algo de la forma "x->y"
se traduce por algo de la forma "(*x).y".
MUCHO CUIDADO: "->" se usa para PUNTEROS, en cambio, '.' se usa para la
ESTRUCTURA en sí misma. OJO: "->" va junto, no separado. "- >" está mal. */
}
/* Ciertamente, puede haber varias encarnaciones de una misma estructura. En
ese sentido, una estructura vendría a ser el molde con el que se encarna cada
variable de su tipo. Por ser encarnaciones distintas, sus campos no colisionan
entre sí. Conceptualmente, las estructuras (en C) son algo similares a las
clases, pero mucho más rústicas: no poséen métodos, no porporcionan herencia ni
polimorfismo, no soportan variables estáticas, etc. Tan sólo permiten agrupar
campos. */
{
struct cliente c1, c2, *c3;
c3 = &c1;
strcpy(c3->nombre, "Felipe");
c3->edad = 17;
strcpy(c3->direccion, "Las Palmas");
c3 = &c2;
strcpy(c3->nombre, "Carlos");
c3->edad = 71;
strcpy(c3->direccion, "San Felipe");
printf("c1: nombre = \"%s\", edad = %i, direccion = \"%s\"\n",
c1.nombre, c1.edad, c1.direccion);
/* Imprime: nombre = "Felipe", edad = 17, direccion = "Las Palmas" */
printf("c2: nombre = \"%s\", edad = %i, direccion = \"%s\"\n",
c2.nombre, c2.edad, c2.direccion);
/* Imprime: nombre = "Carlos", edad = 71, direccion = "San Felipe" */
}
/* También se puede obtener la dirección de cada uno de los miembros, los
cuales están ubicados consecutivamente en memoria, uno después del otro, según
el orden en que fueron declarados: */
{
struct cliente c;
char *nombre_p = c.nombre; /* OJO: c.nombre ya es un puntero */
int *edad_p = &c.edad; /* '&' tiene menor precedencia que '.' */
char *direccion_p = c.direccion;
strcpy(nombre_p, "Gabriel");
*edad_p = 45;
strcpy(direccion_p, "Las Rejas");
/* Nótese que la dirección del primer campo es la misma dirección que la de
la estructura completa. */
if (&c == nombre_p)
printf("nombre_p apunta al comienzo de c\n");
printf("Los punteros son, en orden creciente: (nombre_p, edad_p, direccion_p"
") = (%p, %p, %p)\n", nombre_p, edad_p, direccion_p);
/* Nota: Los strings consecutivos CONSTANTES se concatenan en tiempo de
COMPILACIÓN. Así, "a" "b" se transforma automáticamente al COMPILAR en "ab".
Esto sirve para imprimir mensajes largos sin tener que invocar printf en
cada línea. */
}
/* Las estructuras, como cualquier otra variable, pueden ser dadas por
argumento a funciones y pueden ser retornadas por ellas. */
struct cliente new_cliente(char *nombre, int edad, char *direccion) {
struct cliente c;/* Crea un cliente <c> local a la función */
strcpy(c.nombre, nombre);
c.edad = edad;
strcpy(c.direccion, direccion);
return c;/* Retorna una COPIA de <c> */
}/* Acá muere <c> */
char *segundo_nombre(struct cliente c) {
int init, largo_nombre;
largo_nombre = strlen(c.nombre);
for (i = 0; i < largo_nombre && c.nombre[i] != ' '; i++)
;/* Se puede subindicar al campo porque "[]" tiene más prioridad que '.' */
return cliente.nombre + i;/* Puntero al segundo nombre (si lo hay) */
}
int main() {
struct cliente c = new_cliente("Víctor Andrés", 20, "Salamanca");
printf("El segundo nombre es \"%s\"\n", segundo_nombre(c));
return 0;
}
/* El problema de este enfoque es que en cada llamada y retorno se pasa una
copia completa de la estructura, lo cual es tremendamente ineficiente, lo que
es peor para estructuras grandes como ésta. Además, no permite modificar datos
en forma externa: */
void cambiar_nombre(struct cliente c, char *nuevo_nombre) {
strcpy(c.nombre, nuevo_nombre);
/* Nota: Como <nombre> es un puntero al arreglo de caracteres del nombre del
cliente y dicho campo es el primero, podría haberse hecho:
strcpy((char *)&c, nuevo_nombre);
Pero sería menos claro porque se necesitaría saber que <nombre> es el
primer campo de la estructura. Además, se necesitó hacer un cast para evitar
un warning, ya que &c es de tipo (struct cliente *) y strcpy espera un
parámetro de tipo (char *).
Por otra parte, se debió usar strcpy y no una simple asignación por dos
motivos:
1) <nombre> es de tipo char[], así que no se puede hacer apuntar a otra parte
que no sea el arreglo de caracteres que está dentro de la estructura.
2) Eventualmente, el string apuntado por nuevo nombre podría ser reutilizado
fuera de la función (por ejemplo, cambiando su contenido o liberándolo), lo
que afectaría colateralmente a la estructura. Para evitarlo, se saca una
copia del nombre. Nótese que se asume que el nuevo nombre cabe en el
espacio que la estructura tiene dedidado para ello. */
}
int main() {
/* Aprovechamos de introducir el concepto de inicalización estática: Al
crear una estructura, se pueden inicializar sus campos inmediatamente con
datos CONSTANTES, que se calculan en tiempo de COMPILACIÓN, encerrando los
valores entre llaves y poniéndolos en el mismo orden en que fueron declarados
los parámetros. */
struct cliente c = {"Claudio", 15, "Pelotillehue"};
/* Equivale a:
struct cliente c;
strcpy(c.nombre, "Claudio");
c.edad = 15;
strcpy(c.direccion, "Pelotillehue");
*/
printf("El nombre antiguo es \"%s\"\n", c.nombre);/* "Claudio" */
cambiar_nombre(c, "Francisco");
printf("El nombre nuevo es \"%s\"\n", c.nombre);/* "Claudio" */
/* No se cambió el nombre, porque lo que se pasó al invocar a la función fue
una COPIA de la estructura, no un puntero. */
return 0;
}
/* Para que funcionen los efectos colaterales, se puede redefinir la función de
modo que use punteros. */
void cambiar_nombre(struct cliente *c, char *nuevo_nombre) {
strcpy(c->nombre, nuevo_nombre);
}
int main() {
struct cliente c = {"Claudio", 15, "Pelotillehue"};
printf("El nombre antiguo es \"%s\"\n", c.nombre);/* "Claudio" */
cambiar_nombre(&c, "Francisco");
printf("El nombre nuevo es \"%s\"\n", c.nombre);/* "Francisco" */
/* Ahora sí se modificó la estructura porque lo que se pasó fue una COPIA DEL
PUNTERO, NO DE LA ESTRUCTURA, así que se pudo obtener efectos colaterales
mediante el uso de dicho puntero. Recuerden que en C TODAS las llamadas son
por COPIA (o por VALOR) de los argumentos (al igual que los retornos de las
funciones). Lo que se debe tener muy claro es QUÉ SE COPIA: el PUNTERO o el
VALOR. */
return 0;
}
/* El problema de este enfoque hasta el momento es que las estructuras mueren
al terminar el bloque que las crea. Como sabemos, para hacer que un objeto siga
viviendo más allá del término del bloque en el que se crea, debe ser localizado
en el heap, o sea, utilizando memoria dinámica. Para ello, hay que usar la
función de biblioteca malloc(). Como toda memoria pedida con malloc(), después
deberá ser liberada con free() utilizando al puntero dado por malloc(). */
struct cliente *new_cliente(char *nombre, int edad, char *direccion) {
struct cliente *c;/* El puntero <c> inicialmente apunta a cualquier parte */
/* Se inicializa el puntero para que apunte a una localidad de memoria
válida con tanto espacio como sea necesario para alojar la estructura. */
c = (struct cliente *)malloc(1 * sizeof(struct cliente));
/* OJO: sizeof(struct cliente) >= suma{sizeof(campos)} ¿Por qué? Porque en
algunas arquitecturas, se necesita que las variables estén alineadas en
memoria según múltiplos de, por ejemplo, 8 bytes, así que puede haber espacio
muerto entre campos que no se use, pero que se debe saltar para alinear los
campos. En tal caso, el tamaño de la estructura total será más grande que la
suma de los tamaños de cada uno de sus campos. */
if (NULL == c)/* Siempre un malloc puede dar NULL */
return NULL;
/* Ahora <c> apunta a un lugar válido de memoria, pero dicho lugar puede
contener basura, así que hay que inicializarlo: */
strcpy(c->nombre, nombre);
c->edad = edad;
strcpy(c->direccion, direccion);
return c;/* Retorna un PUNTERO (poquitos bytes), no la estructura */
}
int main() {
struct cliente *c = new_cliente("Cristian", 40, "Recoleta");
if (NULL == c)
return 1;/* No hay memoria */
printf("nombre = \"%s\", edad = %i, direccion = \"%s\"\n",
c->nombre, c->edad, c->direccion);
free(c);/* Hay que liberar el puntero porque apunta a memoria dinámica */
return 0;/* OK */
}
/* Otro de los problemas de este enfoque es que la estructura es demasiado
grande, debido a que cada una debe contener espacio para cada arreglo, los que
son muy largos. Una solución para el tamaño sería redefinir la estructura de
modo que utilice punteros en vez del arreglo completo adentro de ella. */
struct cliente_p {
char *nombre;
int edad;
char *direccion;
}; /* No olvidar el ';' */
struct cliente_p *new_cliente_p(char *nombre, int edad, char *direccion) {
struct cliente_p *c;/* Puntero a un <cliente_p>. Apunta a cualquier lado */
/* Reserva espacio para un <cliente_p> y deja a <c> apuntando al espacio. */
c = (struct cliente_p *)malloc(1 * sizeof(struct cliente_p));
/* El espacio tiene basura. Cada campo contiene basura. Los campos que son
punteros apuntan a cualquier lado. */
if (NULL == c)/* Si no hay espacio para la estructura */
return NULL;
/* Reserva espacio para el nombre y deja al puntero apuntando al espacio */
c->nombre = (char *)malloc(strlen(nombre) * sizeof(char) + 1);
/* <nombre> pudo ser modificado porque es de tipo (char *) y no (char[]). */
if (NULL == c->nombre) {/* Si no hay espacio para el nombre */
free(c);/* Libera la estructura (si no se hace, hay memory-leak) */
return NULL;
}
/* Reserva espacio para la dirección y deja al puntero apuntando al espacio */
c->direccion = (char *)malloc(strlen(direccion) * sizeof(char) + 1);
/* <direccion> pudo ser modificado porque es de tipo (char *) y no (char[]).*/
if (NULL == c->direccion) {/* Si no hay espacio para la dirección */
free(c->nombre);/* Libera el nombre (si no, hay memory-leak */
free(c);/* Libera la estructura */
/* OJO: La liberación se hace en ESE orden, ya que si se hiciera al revés:
free(c);
free(c->nombre);
Al hacer la segunda liberación, se estaría accediendo al valor almacenado
en un área de memoria ya liberada. */
return NULL;
}
/* Copia el nombre al espacio apuntado por c->nombre, el cual es válido y
permite escribir en él. */
strcpy(c->nombre, nombre);
c->edad = edad;/* Inicializa la edad */
/* Copia la dirección al espacio apuntado por c->direccion, el cual es válido
y permite escribir en él. */
strcpy(c->direccion, direccion);/* Copia la dirección */
return c;/* Retorna el puntero */
}
void cambiar_nombre_p(struct cliente_p *c, char *nuevo_nombre) {
strcpy(c->nombre, nuevo_nombre);
}
int main() {
/* Declara un puntero a un <cliente_p>. Inicialmente apunta a basura */
struct cliente_p *c;
/* Crea dinámicamente un cliente, lo inicializa y deja a <c> apuntando a él */
c = new_cliente_p("Sebastián", 40, "Pudahuel");
printf("%s\n", c->nombre);/* Sebastián */
/* Pasa un puntero a la estructura y le cambia el nombre */
cambiar_nombre_p(c, "Hugo");
printf("%s\n", c->nombre);/* Hugo */
free(c);/* Hay que liberar lo pedido dinámicamente */
return 0;
}
/* Así como la visibilidad de las variables depende de dónde sean declaradas,
del mismo modo ocurre con las estructuras. Las variables declaradas afuera de
las funciones son visibles desde ahí en adelante para todas las funciones del
archivo. Las variables declaradas al interior de una función son visibles tan
sólo al interior de ella y dejan de ser visibles cuando termina. Con las
estructuras pasa lo mismo. La estructura "cliente", hasta acá, ha tenido
visibilidad global porque ha sido declarada al exterior de las funciones. Si
se declarase adentro, sería visible sólo en su interior. También cabe mencionar
que en C, todo lo declarado al interior de una función oculta a lo declarado
afuera mientras se ejecuta la función; así se ve sólo la definición más
cercana. */
struct punto {/* Visible globalmente */
double x;
double y;
};
/* Trabaja con "struct punto" global */
struct punto new_punto(double x, double y) {
struct punto p;
p.x = x; p.y = y;
return p;
}
int main() {
struct punto {/* Definición local */
int x;
int y;
};
struct punto p, q;/* struct punto local */
p.x = p.y = 1;
q.x = 2;
q.y = 3;
q.y = q.y / 2; /* Da 1 porque es división de enteros */
return 0;
}
/* Se puede aprovechar para definir una variable de tipo struct apenas se
define la estructura, de modo que no se tenga que escribir de nuevo el
encabezado: */
struct punto {
int x;
int y;
} p, q, *r, s;
/* Esto declara "struct punto" y la define con los campos enteros <x> e <y>. De
paso, aprovecha de declarar las variables <p>, <q> y <s> como estructuras de
ese tipo y define a <r> como un puntero a una estructura de ese tipo.
Naturalmente, dichas variables serán globales o locales según sean definidas
afuera o dentro de alguna función, respectivamente.
Por otra parte, también puede ser de interés el declarar una estructura
anónima, es decir, sin nombre. ¿Para qué? Porque a veces tan sólo se necesita
agrupar datos y no darles nombre. Eso sí, dado que no se le dió nombre a la
estructura, no se podrá volver a referir a ella, así que las variables deben
ser declaradas inmediatamente con el esquema recién mostrado: */
struct {/* Estructura anónima */
int x;
int y;
} p, q, *r, s;
/* Se debe declarar a las variables de inmediato porque después no se podrá
usar el nombre de la estructura para hacerlo (porque no tiene). Cabe destacar
que son optativos o bien el nombre de la estructura, o bien las variables que
se crean en el momento, pero no ambas (porque no serviría de nada). */
/* No sirve de nada (y está malo) */
struct {
int x;
int y;
};/* ¿Qué variables crea? ¿Cómo se pueden crear otras variables de este tipo? */
/* Una estructura puede albergar a su interior cualquier tipo de dato, en
particular, a otra estructura, o sea, puede haber estructuras anidadas; así que
una declaración como la que sigue sería válida: */
struct cliente {
char *nombre;
int edad;
struct dir {
char *calle;
char *comuna;
} direccion;
};
/* Esto permite agrupar datos según alguna relación común. En este caso, el
nombre de la calle y de la comuna en la dirección. En este caso, <dir> es el
nombre de la estructura que alberga a <calle> y <comuna>, pero la variable
creada se llama <direccion>, así que para acceder a sus campos, se haría así:
*/
{
struct cliente c, *c_p;
strcpy(c.nombre, "Claudia");
c.edad = 50;
strcpy(c.direccion.calle, "Alameda");
strcpy(c.direccion.comuna, "Santiago");
c_p = (struct cliente *)malloc(1 * sizeof(struct cliente));
if (NULL == c_p)
exit(1);
strcpy(c_p->nombre, "Francisca");
c_p->edad = 30;
strcpy(c_p->direccion.calle, "Santo Domingo");
strcpy(c_p->direccion.comuna, "Santiago")
}
/* Nótese que cuando algo es puntero, se accede a sus campos con "->" y que
cuando es estructura, con ".". Nótese también que el nombre del campo es
"direccion", y no "dir". "dir" es el nombre de la estructura. Este es un
ejemplo de cuándo no se necesita ponerle nombre a una estructura y conviene
usarla anónima. Una mejor definición de "struct cliente" sería la siguiente: */
struct cliente {
char *nombre;
int edad;
struct {/* Anónima */
char *calle;
char *comuna;
} direccion;/* Necesario: es el nombre del campo */
};
/* ¿Y para qué son las estructuras? Pues para poder implementar estructuras de
datos de forma cómoda, de modo que no se tenga que estar arrastrando variables
sueltas de un lado para otro. A continuación, se presenta una implementación de
una pila con listas enlazadas, al "estilo C" (o sea, preocupándose de la
memoria dinámica). En esta ocasión, esta pila almacenará <double>s: */
struct eslabon;/* DECLARA que existirá un "struct eslabon" */
struct eslabon {/* DEFINE la estructura "struct eslabon" */
double valor;
struct eslabon *anterior;/* PUNTERO a una estructura del mismo tipo */
}
/* IMPORTANTE: Al definir una estructura, ésta no puede contener dentro de sí
a la misma estructura que se está tratando de definir, ya que se provocaría una
recursión infinita, ya que nunca se podría terminar de calcular el tamaño que
deberá tener la estructura. Lo que se debe hacer es declarar un PUNTERO a la una
estructura del mismo tipo (de lo cual sí se conoce el tamaño).
OJO: Como se está DEFINIENDO recién "struct eslabon", si no se hubiera declarado
su nombre previamente, no podría utilizarse a su interior un puntero a una
estructura del mismo tipo porque aún no ha sido declarada. Para hacer que el
nombre de la estructura sea conocido de antes, se hace esa DECLARACIÓN antes
de definirla, de modo que al utilizar el nombre al interior de la estructura,
éste ya sea conocido por el compilador y lo valide.
Recuerden que el compilador de C va conociendo los nombres a medida que van
apareciendo en el archivo y sólo permite usar nombres ya DECLARADOS. Si algo no
se ha declarado, se declara automáticamente una vez terminada su DEFINICIÓN. Por
eso no se puede usar el mismo nombre al interior si no se ha declarado: porque
no se ha terminado de definir, así que no se ha producido la declaración
automática; por consiguiente, el nombre no es aún conocido. */
struct pila {
unsigned long long tamano;
struct eslabon *tope;
}
struct pila *pila_new() {
/* Crea el encabezado de la pila */
struct pila *p = (struct pila *)malloc(1 * sizeof(pila));
if (p == NULL)/* Si no hay memoria */
return NULL;
p->tamano = 0;/* Inicializa el tamaño */
p->tope = NULL;/* Inicializa el tope */
return p;/* Retorna la pila */
}
int pila_push(struct pila *p, double d) {
/* Crea un eslabón */
struct eslabon *e = (struct eslabon *)malloc(1 * sizeof(struct eslabon));
if (e == NULL)/* Si no hay memoria */
return 1;/* No hay memoria */
e->valor = d;/* Recuerda el valor apilado */
e->anterior = p->tope;/* Actualiza la lista */
p->tope = e;/* Actualiza el tope */
p->tamano += 1;/* Actualiza el tamaño */
return 0;/* OK */
}
double pila_pop(struct pila *p) {
double d = p->tope.valor;/* Rescata el valor en el tope */
struct eslabon *e = p->tope;/* Recuerda el antiguo tope */
p->tope = e->anterior;/* Actualiza el tope */
p->tamano -= 1;/* Actualiza el nuevo tamaño */
free(e);/* Libera el antiguo eslabón que ya no se necesita. Si no se hace, hay
memory-leak porque nunca más se podrá liberar. */
return d;/* Retorna el número que estaba en el tope */
}
int pila_isEmpty(struct pila *p) {
return p->tope == NULL;
}
unsigned long long pila_getSize(struct pila *p) {
return p->tamano;
}
void pila_free(struct pila *p) {
while (p->tope != NULL) {/* Mientras haya eslabones */
struct eslabon *e = p->tope;/* Recuerda el tope actual */
p->tope = e->anterior;/* Actualiza el tope */
free(e);/* Libera el antiguo tope */
}/* Siguiente eslabón */
free(p);/* Libera la pila */
}
int main() {
double numeros[] = {3.5, 4.0, 2.9};
int i;
struct pila *p = pila_new();
if (p == NULL) {
fprintf(stderr, "No hay memoria para crear la pila.\n");
exit(1);
}
for (i = 0; i < 3; i++) {
if (pila_push(p, numeros[i])) {
fprintf(stderr, "No hay memoria para apilar el número %f.\n", numeros[i]);
pila_free(p);
exit(1);
}
printf("Número %f apilado.\n", numeros[i]);
}
while (!pila_isEmpty(p)) {
printf("Desapilando elemento %f.\n", pila_pop(p));
}
pila_free(p);
return 0;
}