Está en la página 1de 8

Declaración de estructuras

Sinopsis

Lo mismo que con el resto de las clases, el proceso de usar una estructura C++ comprende
en realidad tres pasos: definir la clase; crear un objeto, e iniciar el objeto.

Definir la clase

En contra de lo que ocurre con el resto de los tipos, que están pre-definidos en el lenguaje.
Por ejemplo, al declarar char ch; ya se sabe exactamente que cosa es ch, en este caso hay
que definir previamente el tipo. Los tipos estructura se declaran mediante la palabra clave
struct. Sería algo así como:

struct Punto;
struct Punt2 {int x; int y; int z; };

La primera sentencia es una declaración incompleta (ver más abajo); la segunda es una
definición completa de una nueva clase tipo struct denominado Punt2; tiene tres
componentes perfectamente definidos (tres int: x, y, z respectivamente). Como puede
verse, los componentes se determinan de forma análoga a los parámetros de las funciones,
determinando tipo y nombre. Aquí es imprescindible el punto y coma ";" para separar los
miembros (sin olvidar poner otro después del último).

2.1 Ya hemos señalado que en C++ las estructuras son un tipo de clases; entonces, si
nos referimos a la terminología de la POO diríamos que en esta fase estamos
definiendo la clase. El conjunto de declaraciones dentro de los corchetes {...; ...; ...; }
declara los nombres y tipos de sus miembros. Los miembros pueden ser de cualquier
tipo con una excepción:

2.2 Un miembro no puede ser la estructura que se declara porque daría lugar a una
declaración circular (lo definido está dentro de su definición). Ejemplo:

struct mystr { mystr s }; // Ilegal

2.3 Uno, o varios, de los miembros puede ser un puntero a la estructura que se está
declarando. Ejemplo:

struct mystr { mystr *ps } // Ok: Correcto


Nota: esta interesantísima posibilidad constituye la base de estructuras auto
referenciadas; una técnica de programación que tiene amplias posibilidades de
aplicación. Por ejemplo, listas enlazadas y árboles.

2.4 También es posible que los miembros de una estructura sean a su vez estructuras
previamente definidas, dando lugar a estructuras anidadas. Por ejemplo:

struct Punto {
int x; int y;
};
struct Linea {
struct Punto p1;
struct Punto p2;
} c1;

declara Linea como un tipo struct con dos miembros, cada uno de los cuales es un tipo
struct Punto.

Nota: en C, un miembro no puede ser del tipo "función devolviendo...". Es decir, las
funciones no pueden ser miembros de la estructuras C (de lo contrario serían clases). En
cambio, sí están admitidos los "punteros a función devolviendo..." (ya que son
variables, no métodos). Las estructuras C++ sí pueden incluir funciones miembro (de
hecho son clases), además, en C++ la palabra struct puede omitirse.

2.5 Es importante tener en cuenta que, en este punto (definición), solo está permitido
señalar tipo y nombre de los miembros, sin que se pueda efectuar ninguna asignación;
ni aún en el caso de que se trate de una constante [1]. Por ejemplo, las definiciones que
siguen serían ilegales, pues todas implican una asignación en la definición del segundo
miembro:

struct Str {int x; int y = 2; }; // Error!


struct Str {int x; const int y = 3; }; // ídem
struct Str {int x; char c = 'X'; }; // ídem
struct Str {int x; char * st = "Hola"; }; // ídem
struct Str {int x; "Hola"; }; // ídem
struct Str {int x; int a[2] = {3, 4};}; // ídem

.
Crear un objeto (instanciar la clase)

Un segundo paso es declarar una variable como perteneciente al nuevo tipo. Del mismo
modo que para declarar una variable ch como de tipo char declarábamos: char ch;, en este
caso, para declarar una variable st como estructura tipo punto se utiliza:

struct Punto pt; // C y C++

En C++ no es necesario señalar que Punto es una estructura (suponemos que ya lo sabe el
compilador por las sentencias anteriores), de forma que si no existe ninguna otra variable
punto en el mismo ámbito de nombres, no hay ambigüedad y se puede poner directamente:

Punto pt; // C++

Usando la terminología de la POO diríamos que estamos instanciando la clase, es decir,


creando un objeto concreto pt, con espacio en memoria, que pertenece a (es derivado de)
dicha clase. Dichos objetos sí pueden ser asignados con valores concretos en sus miembros.

3.1 Advertir que cada declaración de (tipo de) estructura introduce un nuevo tipo,
distinto de los demás (una nueva clase), de forma que las declaraciones:

struct StA {
int i,j;
} a, a1;
struct StB {
int i,j;
} b;

definen dos tipos de estructura distintas: StA y StB; los objetos a y b1 son del tipo StA,
pero a y b son de tipo distinto.

Desde la óptica de la POO la frase anterior se enunciaría como sigue: "definen dos clases
distintas, StA y StB; los objetos a y b1 son instancias de StA, pero b lo es de la clase StB.
Por tanto, a y b son objetos de tipo distinto.

3.2 De la misma forma que ocurre con el resto de variables, puede declararse más de un
elemento en la misma sentencia:

struct Punto p1, p2, p3,... pn;

sin embargo, de una variable de tipo estructura no es posible derivar nuevas variables, es
decir no es lícita la sentencia que sigue (utilizando la terminología de C++ lo
enunciaríamos diciendo: de un objeto no puede instanciarse otro objeto).
struct p1, p11, p12, p13,... p1n; // NO!!

Iniciar el objeto

Un tercer paso es inicializar dicha variable. Del mismo modo que para iniciar ch se
realizaba una asignación del tipo: ch = 'x', en este caso se utiliza la asignación (análoga a la
de matrices):

st = { 12, 25, 3};

Los pasos 2 y 3 se pueden realizar en la misma sentencia. Por ejemplo:

struct punto st = { 12, 25, 3};

También puede realizarse los tres pasos en una misma sentencia:

struct punto { int x; int y; intz; } p1 = { 12, 25, 3};

Una vez definido el nuevo tipo, pueden declararse punteros y matrices de estructuras de
dicho tipo. Por ejemplo:

struct stA { ... }; // define la estructura tipo stA


struct stA st, *pst, arst[10];

La segunda línea declara que st es una estructura de tipo stA; que pst es un puntero a dicho
tipo, y que arst es un array de 10 estructuras tipo stA.

4.1 Como se verá a continuación , es posible incluso declarar estructuras sin asignar un
nombre al tipo correspondiente. Por ejemplo:

struct { ... } st, *pst, arst[10];

Espacio de nombres de estructuras

Los nombres de tipos de estructura comparten el espacio de nombres con las uniones y
enumeraciones (las enumeraciones dentro de una estructura están en un espacio de
nombres diferente). Ver L.4 y L.11 en el ejemplo.

Los nombres de estructura están en un espacio de nombres diferente que el de nombres de


tipos de estructura y nombres de etiqueta (en C++ solo si no tienen un constructor). Ver L.8
y L.18 del ejemplo que sigue.
Los miembros de estructuras disponen de un espacio de nombres cerrado y particular
(espacio de nombres de miembros de clases), de forma que sus nombres se pueden repetir
con los de miembros de otras estructuras o con los de otros espacios (ver L.6, L.7, L.12 y
L.16 del ejemplo ).

La consecuencia es que dentro del mismo ámbito, los nombres de estructuras y uniones
deben ser únicos (ambos comparten el espacio de nombres de clases), aunque no hay
inconveniente que compartan los nombres con los miembros de los otros tres espacios: El
de etiquetas; el de miembros (de clases) y el de enumeraciones [3].

5.1 Ejemplo

goto s;
...
s: // L.3 Etiqueta
struct s { // L.4 OK: nombres de tipo_de_estructura y etiqueta en
// espacios diferentes
int s; // L.6 OK: nombres miembro de estructura en espacio privado
float s; // L.7 ILEGAL: nombre de miembro duplicado
} s; // L.8 OK: nombre_de_estructura en espacio diferente de
// nombre de etiqueta (L.3) y de tipo_de_estructura (L4)
// En C++, esto solo es posible si s no tiene un constructor
union s { // L.11 ILEGAL: nombre duplicado con el de tipo s (L.4)
int s; // L.12 OK: nuevo espacio de miembros
float f;
} f; // L.14 OK: espacio diferente que el de miembros (ver L.8)
struct t {
int s; // L.16 OK: nuevo espacio de miembros
...
} s; // L.18 ILEGAL: nombre duplicado con el de estructura s (L8)

Declaraciones incompletas

Las declaraciones incompletas, conocidas también como declaraciones anticipadas o


adelantadas; consisten en que un puntero a una estructura tipoA puede aparecer en la
declaración de otra, tipoB antes que tipoA haya sido declarada. Ejemplo:

struct A; // declaración anticipada


struct B { struct A *pa };
struct A { struct B *pb };

En este caso, la primera declaración de A se denomina incompleta o anticipada, porque no


existe definición para ella (cuales son sus miembros). Este tipo de declaración sería
correcta aquí, porque en la declaración de B no se necesita conocer el tamaño de A (solo el
de un puntero a estructura).

1. Hay que advertir que en C++ es muy frecuente utilizar typedefs en la declaración de
estructuras. De hecho, los ficheros de cabecera de los compiladores C++ están
repletos de ellos. Es muy frecuente que utilicen expresiones como [4]:

typedef struct {
unsigned char *curp; // Current active pointer
unsigned char *buffer; // Data transfer buffer
int level; // fill/empty level of buffer
int bsize; // Buffer size
unsigned short istemp; // Temporary file indicator
unsigned short flags; // File status flags
wchar_t hold; // Ungetc char if no buffer
char fd; // File descriptor
unsigned char token; // Used for validity checking
} FILE; // This is the FILE object

Por tanto, es posible escribir sentencias como:

#include <stdio.h>

int main(void) {
FILE *in, *out; // define punteros a estructuras FILE
...

Sin embargo, dentro del mismo ámbito de nombres, C++ no permite que una estructura (o
clase) tenga el mismo nombre que un typedef declarado para definir un tipo diferente.
Ejemplo:

typedef void (*C)();


...
class C { // Error declaración múltiple para C.
...
};

Tipos anónimos

El nombre de un tipo de estructura puede omitirse, teniéndose lo que se llama un tipo


anónimo (sin nombre). Pueden utilizarse en declaraciones de componentes separados por
comas cuando se quiere indicar que los componentes son (o derivan de) un tipo de
estructura determinado. Presenta el problema de que al no poderse referir más al tipo (por
no tener nombre), no pueden declararse más objetos adicionales en ningún otro sitio.
Ejemplo:
struct { ..; ..; ..; } s, *ps, arrs[10]; // tipo sin nombre

Es posible crear un typedef al mismo tiempo que se declara una estructura, con o sin
nombre, como se ve en los ejemplos. Generalmente no se necesitan un typedef y un
nombre al mismo tiempo, ya que cualquiera de ellos sirve para las declaraciones.

typedef struct mystruct { ..;..; } MST;


MST s, *ps, arrs[10]; // igual que struct mystruct s, etc.
typedef struct { ..; ..; } YST; // sin nombre
YST y, *yp, arry[20];

Cuando son miembros de clases, las estructuras y uniones anónimas son ignoradas durante
la inicialización.

Nota: el ANSI C++ solamente permite estructuras anónimas que declaren un objeto. Por su
parte, el ANSI C no permite estructuras anónimas, y el compilador C++ de Borland permite
varios tipos de estructuras anónimas que extienden el estándar ANSI.

8.1 Debido a que no hay propiedades de instancia, la sintaxis C++ para las estructuras
anónimas no permite referenciar un puntero this. Por consiguiente, mientras que una
estructura C++ puede tener funciones miembro, las estructuras anónimas C++ no pueden
tenerlas. Los miembros de las estructuras anónimas pueden ser accedidos directamente en
el ámbito en que las estructuras han sido declaradas sin la necesidad de utilizar la sintaxis
x.y o p->y. Por ejemplo:

struct my_struct {
int x;
struct {
int i;
};
inline int func1(int y) { return y + i + x; }
} S;

int main() {
S.x = 6; S.i = 4;
int y = S.func1(3);
printf(“y is %d“, y);
return 0;
}

8.2 Estructuras anónimas anidadas

Borland C++ permite estructuras anónimas y que no sean usadas para declarar un objeto o
cualquier otro tipo. Tienen la forma que se indica:
struct { lista-de-miembros };

Las estructuras anónimas pueden ser anidadas, es decir, declaradas dentro de otra
estructura, unión o clase. En estos casos, la estructura externa debe tener nombre. Por
ejemplo:

struct my_struct {
int x;
struct { // estructura anónima anidada dentro de my_struct
int i;
};
inline int func1(int y);
} S;

Para compatibilidad con el compilador Visual C++ de Microsoft, el compilador Borland C+


+ permite declarar una estructura con nombre que no declara ningún objeto. Estas
estructuras huérfanas deben ser anidadas. Por ejemplo:

struct Direccion {
char calle[25];
int numero;
};

struct Persona {
char nombre[35]
struct Direccion; // estructura huérfana
} candidato; // una instancia de Persona
candidato.numero = 10;

[1] Si nos referimos a C++, se podrían hacer asignaciones iniciales, pero a través del
constructor de la clase.

[3] No confundir los nombres de las enumeraciones con los nombres de los enumeradores.

[4] Definición tomada del fichero de cabecera <stdio.h> de la Librería Estándar.

También podría gustarte