Structs, tipos y listas enlazadas

Cada vez que retomo la programación en C después de haberme dado más de un mes para oxidarme, hay algo que, por más que lo utilice, siempre tengo que volver a consultar: las structs. Una struct nos permite tener un registro con varios campos en una sola variable, pero no voy a explicar mucho más aquí. Voy a hablar de las distintas formas en las que se pueden declarar, que es lo que siempre olvido.

La declaración de una struct tiene la forma struct nombre_opcional { campos };. La forma más usual, al menos en mi caso, de declarar una struct y luego definir una variable con ese tipo es esta:

C:
  1. struct complejo {
  2.     float real, imaginario;
  3. };
  4.  
  5. struct complejo n;

En ella creo una struct simple con dos campos, que son la parte real e imaginaria de un número complejo, y declaro la variable n como complejo. Aquí puede verse como el uso de una struct es similar al de un tipo, pero hay que recordar que es necesario que la declaración de la variable esté precedida de la palabra struct, como se ve en el ejemplo, pues los nombres de las structs no se incluyen de forma independiente en la tabla de símbolos del compilador, sólo asociados al modificador struct.


Si no vamos a usar muchas variables del mismo tipo, podemos hacer la declaración de n en un sólo paso, y podemos incluso omitir el nombre de la struct (por eso es opcional), como se ve en este otro ejemplo:

C:
  1. struct {
  2.     float real, imaginario;
  3. } n;

En este caso la variable n es también un número complejo, pero si quisiéramos otra variable del mismo tipo en otro lugar del programa, tendríamos que reescribir toda esta declaración.

Si queremos evitar evitar tener que pasar por el tedioso trabajo :P de escribir la palabra struct cada vez que vamos a declarar una variable con el tipo struct complejo, podemos usar typedef. Typedef es una sentencia que permite crear nuevos nombres de tipo a partir de los tipos ya existentes. Su sintaxis es typedef tipo_anterior alias. Esto lo hacemos normalmente porque el tipo original es más complicado de escribir o porque un nuevo nombre añade información semántica de valor y hace el código más legible. Siguiendo con el ejemplo de los números complejos, podría crear un tipo llamado simplemente complejo de la siguiente forma:

C:
  1. typedef struct {
  2.     float real, imaginario;
  3. } complejo;
  4.  
  5. complejo n;

Como vemos, en este caso se omite el nombre de la struct, de modo que es anónima, pero no nos hará falta porque, como estamos definiendo un nuevo tipo, podemos referirnos a ella con el nombre de éste: complejo. Hay que prestar especial atención al hecho de que, en esta ocasión, a la hora de declarar la variable n, no es necesario usar la palabra struct, ahora complejo se encuentra en la tabla de símbolos del compilador de forma independiente. En este caso podríamos haber indicado un nombre a la struct, pero no nos habría servido para nada, incluso podría haberse llamado complejo, igual que el tipo, pues al ir asociada a la palabra struct no existirían conflictos de nombres. Más abajo hay un ejemplo que puede aclarar este punto.

Para seguir explicando las diferentes posibilidades voy a dejar de lado los números complejos y usaré para los ejemplos siguientes una de las estructuras clásicas con las que cualquier programador tiene que enfrentarse alguna vez: una lista simplemente enlazada. En este caso usaré una lista de enteros. Las explicaciones, no obstante, son aplicables a cualquier struct que incluya entre sus campos algún puntero a sí misma.

La forma más común de crear esta estructura en la siguiente:

C:
  1. struct nodo {
  2.     int valor;
  3.     struct nodo *siguiente;
  4. };
  5.  
  6. struct nodo *lista;

Ahora, si queremos crear un tipo nuevo para la struct que representa un nodo de la lista, NO podemos hacer esto:

C:
  1. typedef struct {
  2.     int valor
  3.     elem *siguiente;
  4. } elem;
  5.  
  6. elem *lista;

El problema es que el tipo que se está definiendo no se encuentra disponible hasta que se ha terminado de definir (parece lógico, ¿no?), de modo que no podemos crear el puntero siguiente usando el nombre elem, porque en ese momento no se encuentra en la tabla de símbolos. Hay varias formas de resolver esto. La primera, y más usual, es darle un nombre a la struct y usarlo para declarar el puntero siguiente, en lugar de usar el nombre del tipo:

C:
  1. typedef struct nodo {
  2.     int valor;
  3.     struct nodo *siguiente;
  4. } elem;
  5.  
  6. elem *lista;

Pero si esta mezcla no es de tu agrado, hay una forma en la que se puede usar el nombre del nuevo tipo para el puntero siguiente, y es declarando la struct y el tipo primero y definiendo los campos después:

C:
  1. typedef struct nodo elem;
  2.  
  3. struct nodo {
  4.     int valor;
  5.     elem *siguiente;
  6. };
  7.  
  8. elem *lista;

Así, cuando llega la declaración del puntero siguiente, la sentencia que crea el tipo ha concluido y éste se encuentra ya en la tabla de símbolos. Aunque pueda parecer menos directa, es la que más me gusta y creo que la usaré a partir de ahora a menos que el caso concreto aconseje lo contrario.

Siguiendo este último ejemplo, podemos ver en este recorte lo que decía más arriba, que el nombre de la struct no es un identificador por sí sólo, así que podemos usarlo también para definir el tipo:

C:
  1. typedef struct nodo nodo;
  2. struct nodo {
  3.     int valor;
  4.     nodo *siguiente;
  5. } ;
  6.  
  7. nodo *lista;

Ahora lo más probable es que cuando vuelva a pasar un tiempo sin escribir en C, se me olviden muchas cosas excepto esta. En cualquier caso, espero que este post te haya sido de tanta utilidad como a mi ;)

Algunas de mis lecturas para formarme, además del libro The C Programming Language, de Kernighan y Ritchie, han sido estas fuente 1, fuente 2, fuente 3 y fuente 4.

Esta entrada se publicó el Lunes, 13 de Agosto de 2007 a las 4:07 pm y está archivada en Programación con estos tags o etiquetas: , . Puedes seguir las respuestas a esta entrada mediante el feed RSS 2.0. Puedes dejar dejar un comentario, o hacer trackback desde tu propia web.

Un comentario to “Structs, tipos y listas enlazadas”

Hola, amigo. Mil gracias por tu ayuda, me sirvió mucho.

Tu comentario





Los campos en negrita son obligatorios. Las direcciones de email nunca se publican ni se distribuyen.

Se permiten algunas etiquetas de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong> . Las URIs deben ser completas (p. ej.: http://www.domainname.com) y todas las etiquetas deben cerrarse correctamente.

Los saltos de línea y los párrafos se forman automáticamente.

Por favor, escribe comentarios mínimamente relevantes. Los comentarios ofensivos, inapropiados o fuera de tema pueden ser editados o borrados.

Buscar

Posts recientes

  1. Structs, tipos y listas enlazadas
  2. Opciones en las direcciones de Gmail
  3. Python en Windows sin ventana de DOS
  4. Problemas al compilar blas-atlas en Gentoo
  5. La codificación de caracteres en Python
  6. Comprobaciones de particiones en el arranque
  7. Cuidado con Ubuntu en los portátiles Dell
  8. Escoger el orden de tarjetas de sonido con Alsa en Ubuntu
  9. Actualización de Ubuntu: de Dapper a Edgy
  10. ¡Hola mundo!


Licencia de contenidos

Todo el contenido original creado por Jacobo de Vera y que aparece en este sitio web se encuentra, si no se indica lo contrario, protegido por una licencia de Creative Commons.

DreamHost promo

DreamHost

Esta web está alojada en uno de los servidores de DreamHost.

Si buscas alojamiento para tu web quizá te interesen sus precios.

Si te decides y quieres obtener un descuento de 45.00$ en cualquiera de los planes anuales o bienales introduce este código de promoción en el formulario de alta:

JOV042