06.11
Estructuras
Una estructura, en C y C++, es un tipo definido por el usuario que puede contener variables. ¿Qué significa esto. Pues que, igual que utilizamos los tipos int, float, double, y todos los demás, podemos definir nuestros propios tipos, y una forma de hacerlo es con estructuras.
Declaración
La declaración de una estructura comienza por la palabra reservada “struct” seguida (no necesariamente) del nombre de la estructura. A continuación, entre llaves, definimos las variables que va a contener. Por último, la declaración termina en punto y coma, o bien declarando alguna variable del tipo definido
Por ejemplo, si queremos una estructura para representar los números complejos, hacemos:
| Código: | |
| struct complejo { double real; double imaginario; }; |
O bien:
| Código: | |
| struct complejo { double real; double imaginario; } c1, c2; |
Lo que hemos creado es un tipo que engloba dos variables “double”. La definición de una estructura puede hacerse en cualquier parte del código, fuera de main, dentro, dentro de otra estrucutra, en otra función…
Las variables que están englobadas por la estructura, se llamarán a partir de ahora “atributos”.
Crear una variable del tipo definido es tan sencillo como crearla del tipo ‘int’ o ‘float’. Basta con poner “struct”+ nombre de la estructura, seguido de los nombres de las variables creadas. Por ejemplo, para el tipo complejo:
struct complejo c1, c2, c3;
También podemos crear punteros a estructuras, o vectores de estructuras:
struct complejo vc[30], *pc;
Otras declaraciones de estructuras pueden ser:
| Código: | |
| struct fecha { int dia; int mes; int anyo; }; |
| Código: | |
| struct persona { struct fecha nacimiento; char* nombre; int edad; } habitantes[1000]; |
NOTA: Cuando usemos una estructura dentro de otra, como “persona” utiliza la estructura “fecha”, hay que asegurarse de haber definido “fecha” antes que “persona” para evitar errores.
La declaración anterior es equivalente a:
| Código: | |
| struct persona { struct fecha { int dia; int mes; int anyo; } nacimiento; char* nombre; int edad; } habitantes[1000]; |
Acceso a atributos
Una vez hemos definido una variable del tipo definido, podemos acceder a los atributos que la forman del siguiente modo.
Si c1 es una variable definida como complejo (la que definimos arriba), y queremos acceder a su parte real, hacemos:
| Código: | |
| c1.real; |
c1.real es la expresión por la que accedemos al atributo “real” de la variable de tipo complejo “c1”.
Por ejemplo, si queremos instanciar la parte real de c1 a 5 y la parte imaginaria de c1 a 9, hacemos:
| Código: | |
| c1.real = 5; c1.imaginario = 9; |
Para leer el valor de un atributo tampoco hay mayor complicación:
| Código: | |
| c2.imaginario = c1.imaginario-5; |
Hay que entender que “c1.real” puede ser visto como el nombre de una variable, y puede ser usado del mismo modo que una variable normal y corriente.
Recordemos que c1.real no es el mismo dato que c2.real. Las variables “c1” y “c2” están separadas en memoria, y cada una tiene sus propios atributos “real” e “imaginario”, que son del tipo double.
Operaciones con estructuras
Si bien los atributos de una estructura son variables normales como las que estamos acostumbrados a trabajar, la variable que los engloba, que es del tipo definido por el usuario como una estructura, no puede ser usada del mismo modo.
Por ejemplo, dos variables c1 y c2 del tipo struct conjunto (definido arriba) no se pueden sumar. Podemos sumar sus atributos, pero no las variables c1 y c2 en sí mismas.
| Código: | |
| c1.real += c2.real; //Correcto c1.imaginario += c2.imaginario; //Correcto c1 += c2; //Incorrecto |
Una de las pocas operaciones que podemos hacer con las variables c1 y c2, que podemos hacer con cualquier variable de un tipo estructura, es la asignación:
| Código: | |
| c1 = c2; //Correcto |
Al hacer esto, los atributos de c1 toman los valores de los atributos de c2.
Vectores de estructuras
Si tenemos declarado un vector de un tipo definido por el usuario como estructura, como:
| Código: | |
| struct complejo vc[30]; |
Lo que tenemos es una lista de treinta estructuras “complejo”, cada una con sus propios atributos “real” y “imaginario”.
Si queremos acceder al cuarto elemento del array de complejos vc, hacemos:
| Código: | |
| vc[3]; //Recordemos que este es el cuarto, después de vc[0], vc[1] y vc[2]. |
Esto lo que nos da es una estructura “complejo” individual. Podemos tratarla igual que hemos tratado c1 y c2:
| Código: | |
| vc[3] = c1; //Asignamos a vc[3] los valores de c1. vc[3].real = 5; |
Podemos usar cualquier expresión:
| Código: | |
| vc[3].imaginario = vc[7].real * vc[1].real + vc[2].imaginario*vc[5].imaginario; |
O podemos acceder en un bucle:
| Código: | |
| for(int i = 0; i < 30; i++) { vc[i].real = i; vc[30-i].imaginario = i+1; } |
No hay límites a la imaginación.
Punteros a estructuras
Como todo puntero, un puntero a una estructura no es más que una variable que guarda, o “señala” la posición de una estructura.
Ya hemos visto su declaración:
| Código: | |
| struct complejo *pc; |
Y ahora veremos su uso.
Para asignar un objeto “complejo” a este puntero, no hace falta ninguna consideración especial, basta con hacer lo mismo que hacíamos con otros tipos de datos.
| Código: | |
| pc = &c1; |
Si queremos acceder a la variable de tipo “complejo” a que apunta “pc”, tampoco hay ninguna excepción, y actuamos como siempre que queremos acceder al dato apuntado por un puntero:
| Código: | |
| *pc; |
Esto es, la estructura a la que apunta pc.
Por ejemplo, véase la diferencia entre hacer:
1)
| Código: | |
| pc = &c2; |
y hacer
2)
| Código: | |
| *pc = c2; |
En el primer caso hacemos que “pc” apunte a “c2”. En el segundo caso, lo que hacemos es asignar al complejo apuntado por “pc” (sea cual sea) los valores de los atributos de c2.
Para acceder a los atributos de una variable de tipo estructura apuntada por “pc”, sabemos que *pc nos devuelve la estructura apuntada por el puntero, y que para acceder a un atributo se utiliza el punto. Por lo que para acceder a un atributo de la variable apuntada por “pc”, hacemos:
| Código: | |
| (*pc).real |
;
NOTA: Los paréntesis se usan para indicar que primero se accede a la variable apuntada, y luego a su atributo. En caso de faltar los paréntesis daría error porque el procesador creería que tiene que acceder al valor apuntado por el atributo “real” de la variable “pc”, y esto es imposible porque “real” no es un puntero ni “pc” es una variable, sino un puntero.
Pese a que esa es una forma de hacerlo, en la práctica se usa otra mucho más sencilla: El operador flecha (->). La expresión de arriba “(*pc).real”, es equivalente a la siguiente expresión:
| Código: | |
| pc->real; |
El operador flecha nos permite acceder de forma sencilla a un atributo de una variable apuntada por un puntero. Sólo puede ser usada por un puntero, ya que en una variable normal no tendría sentido (daría error al no poder acceder a la variable apuntada, porque las variables normales no apuntan a ninguna variable).
Estructuras y funciones
Una función en C/C++ acepta unas variables como parámetros, y devuelve otra o ninguna. Las variables que acepta tienen que tener unos tipos definidos. Pueden ser enteros, reales, punteros a matrices de enteros… o pueden ser definidos por el usuario, como estructuras.
No hay ninguna excepción a tener en cuenta para tratar estructuras en las funciones, así que mejor pondremos un ejemplo explicativo:
| Código: | |
| //Función que devuelve el módulo del complejo double modulo(struct complejo c); //Como argumento, le pasaremos “c”, del tipo “struct complejo”. Devolverá el módulo de ese complejo, que es la raíz cuadrada de la suma de los cuadrados de “real” e “imaginario”.void main(void) { complejo c; c.real = 3; c.imaginario = 4; printf(“%lf”,modulo(c)); //Como queda claro, lo que pasamos por parámetro es la variable c, que es la que ha sido definida como complejo } double modulo(struct complejo c) |
Este programa ha de devolver por pantalla el número “5”.
Otro ejemplo más estrambótico, sin una razón determinada más que mostrar cómo se pasan y devuelven estructuras a funciones:
| Código: | |
| struct complejo* fc(struct complejo v[], int l); //Esta función acepta un array de complejos y un entero que indica su longitud; y devuelvolverá un //puntero al complejo de mayor módulo del array.void main(void) {} struct complejo* fc(struct complejo v[], int l) //Hacemos que este puntero apunte al primer elemento del array: //Recorremos el array //Devolvemos el puntero: |
No se acostumbren a lo bueno, ¿eh?
. Master, sé que soy repetitivo, pero gracias de nuevo.
No Comment.
Add Your Comment