3.8.2. Usar struct para combinar variables

Un struct es una manera de juntar un grupo de variables en una estructura. Cuando se crea un struct, se pueden crear varias instancias de este «nuevo» tipo de variable que ha inventado. Por ejemplo:

//: C03:SimpleStruct.cpp
struct Structure1 {
  char c;
  int i;
  float f;
  double d;
};

int main() {
  struct Structure1 s1, s2;
  s1.c = 'a'; // Select an element using a '.'
  s1.i = 1;
  s1.f = 3.14;
  s1.d = 0.00093;
  s2.c = 'a';
  s2.i = 1;
  s2.f = 3.14;
  s2.d = 0.00093;
} ///:~

Listado 3.44. C03/SimpleStruct.cpp


La declaración de struct debe acabar con una llave. En main(), se crean dos instancias de Structure1: s1 y s2. Cada una de ellas tiene su versión propia y separada de c, I, f y d. De modo que s1 y s2 representan bloques de variables completamente independientes. Para seleccionar uno de estos elementos dentro de s1 o s2, se utiliza un ., sintaxis que se ha visto en el cápitulo previo cuando se utilizaban objetos class de C++ - ya que las clases surgían de structs, de ahí proviene esta sintaxis.

Una cosa a tener en cuenta es la torpeza de usar Structure1 (como salta a la vista, eso sólo se requiere en C, y no en C++). En C, no se puede poner Structure1 cuando se definen variables, se debe poner struct Structure1. Aquí es donde typedef se vuelve especialmente útil en C:

//: C03:SimpleStruct2.cpp
// Using typedef with struct
typedef struct {
  char c;
  int i;
  float f;
  double d;
} Structure2;

int main() {
  Structure2 s1, s2;
  s1.c = 'a';
  s1.i = 1;
  s1.f = 3.14;
  s1.d = 0.00093;
  s2.c = 'a';
  s2.i = 1;
  s2.f = 3.14;
  s2.d = 0.00093;
} ///:~

Listado 3.45. C03/SimpleStruct2.cpp


Usando typedef de este modo, se puede simular (en C; intentar eliminar el typedef para C++) que Structure2 es un tipo predefinido, como int o float, cuando define s1 y s2 (pero se ha de tener en cuenta de que sólo tiene información - características - y no incluye comportamiento, que es lo que se obtiene con objetos reales en C++). Observe que el struct se ha declarado al principio, porque el objetivo es crear el typedef. Sin embargo, hay veces en las que sería necesario referirse a struct durante su definición. En esos casos, se puede repetir el nombre del struct como tal y como typedef.

//: C03:SelfReferential.cpp
// Allowing a struct to refer to itself

typedef struct SelfReferential {
  int i;
  SelfReferential* sr; // Head spinning yet?
} SelfReferential;

int main() {
  SelfReferential sr1, sr2;
  sr1.sr = &sr2;
  sr2.sr = &sr1;
  sr1.i = 47;
  sr2.i = 1024;
} ///:~

Listado 3.46. C03/SelfReferential.cpp


Si lo observa detenidamente, puede ver que sr1 y sr2 apuntan el uno al otro, guardando cada uno una parte de la información.

En realidad, el nombre struct no tiene que ser lo mismo que el nombre typedef, pero normalmente se hace de esta manera ya que tiende a simplificar las cosas.

Punteros y estructuras

En los ejemplos anteriores, todos los structs se manipulan como objetos. Sin embargo, como cualquier bloque de memoria, se puede obtener la dirección de un objeto struct (tal como se ha visto en SelfReferential.cpp). Para seleccionar los elementos de un objeto struct en particular, se utiliza un ., como se ha visto anteriormente. No obstante, si tiene un puntero a un objeto struct, debe seleccionar un elemento de dicho objeto utilizando un operador diferente: el ->. A continuación, un ejemplo:

//: C03:SimpleStruct3.cpp
// Using pointers to structs
typedef struct Structure3 {
  char c;
  int i;
  float f;
  double d;
} Structure3;

int main() {
  Structure3 s1, s2;
  Structure3* sp = &s1;
  sp->c = 'a';
  sp->i = 1;
  sp->f = 3.14;
  sp->d = 0.00093;
  sp = &s2; // Point to a different struct object
  sp->c = 'a';
  sp->i = 1;
  sp->f = 3.14;
  sp->d = 0.00093;
} ///:~

Listado 3.47. C03/SimpleStruct3.cpp


En main(), el puntero sp está apuntando inicialmente a s1, y los miembros de s1 se inicializan seleccionándolos con el -> (y se utiliza este mismo operador para leerlos). Pero luego sp apunta a s2, y esas variables se inicializan del mismo modo. Como puede ver, otro beneficio en el uso de punteros es que pueden ser redirigidos dinámicamente para apuntar a objetos diferentes, eso proporciona más flexibilidad a sus programas, tal como verá.

De momento, es todo lo que debe saber sobre struct, pero se sentirá mucho más cómodo con ellos (y especialmente con sus sucesores mas potentes, las clases) a medida que progrese en este libro.