martes, 2 de febrero de 2010

Estructuras

Una estructura es un conjunto de variables de los mismos o diferentes tipos. Las estructuras son semejantes a los registros (records) de Pascal.

Los elementos integrantes de una estructura se denominan miembros. Pascal llama campos (fields) a los elementos de una estructura, pero C reserva este nombre para referirse a los miembros particulares de estructuras que permiten manejo de bits.

La declaraciòn de una estructura se hace como:

struct x {

miembros-y-declaraciones

}

utilizándose como un tipo de dato más en declaraciones de tipo de variables.

"x" es el nombre de la estructura. Las referencias posteriores a la misma estructura pueden omitir las llaves ({}) y la declaración de sus miembros.

Los miembros y declaraciones incluidos en la estructura son como cualquier declaración de variable de las vistas hasta ahora, con la única diferencia de que no puede especificarse'un tipo de almacenamiento (auto, static, register, extern). Por ejemplo:

struct fecha {

int dias;

int mes;

int año;

};

struct persona {

char nombre[TAMAÑO1];

long cod_postal;

long ss_num;

double sueldo;

struct date nacim_fecha;

struct date incorp_fecha;

};

serìa equivalente a los records de Pascal:

type

date = record

dia: 1..31;

mes: 1..12;

año: integer;

end;

person = record

nombre: array [1.. TAMAÑO2] of char;

cod_postal: integer;

sueldo: real;

nacim_fecha: date;

incorp_fecha: date;

end;


En este ejemplo definimos una estructura "fecha" conteniendo tres variables enteras: día, mes y año, y definimos una estructura "persona", conteniendo dos arrays de caracteres (nombre y dirección) cod_postal de tipo long, ss_num de tipo long, sueldo de tipo double, y dos miembros consistentes en estructuras "fecha", correspondientes a nacim_fecha e incorp_fecha.

Para acceder a un miembro de una estructura se utiliza el operador ".", como ya anticipábamos al hablar.de las variables en C y sus tipos. Se recurre a él en la forma:

estructura-variable.miembro 4

Por ejemplo, para declarar una variable "d" como una estructura "fecha" e inicializarla a la fecha 1 de junio de 1986, se haría del modo siguiente:

struct fecha d;

d.dia = 1

d.mes = 6;

d.año = 1986;

Como podemos observar en la declaración de la estructura persona las estructuras se pueden encadenar, pudiendo utilizarse como miembros de otras estructuras. Si se define "emp" como una estructura persona:

struct persona emp;

entonces

emp.nacim_fecha.año

se referirá al año de nacimiento del empleado en cuestión.

Una estructura externa o estática se puede inicializar acompañando el nombre de la estructura por una lista de inicializado-res rodeados por llaves ({}).

struct fecha d = {1, 6, 1986};

Declara "d" como una estructura fecha, e inicializa d.día a 1, d.mes a 6 y d.año a 1986.


Operaciones sobre estructuras

En implementaciones antiguas de C las únicas operaciones permitidas sobre las estructuras eran el acceso a sus miembros (mediante un "."), o el acceso a su dirección (mediante un &). ,

Los compiladores más recientes permiten la asignación de variables estructura, incluyendo el paso de estructuras como parámetros y devolviendo estructuras como valores de funciones.

No está permitido realizar comparaciones de estructuras.

Muchos compiladores que admiten la devolución de estructuras lo hacen de manera no-reentrante, dejando el valor de la estructura devuelta en una variable estática en lugar de en el stack.

Las estructuras automáticas no pueden inicializarse a pesar de que inicializar variables automáticas es equivalente a realizar una asignación. En el S.O. Unix se obtendría el mensaje de error "No auto aggregate inicialitation" como respuesta a este intento.


Punteros a estructuras:

Debido a las restricciones impuestas a las variables de tipo estructura normalmente se suelen utilizar punteros a estructuras para realizar el traspaso de estructuras como argumentos de funciones.

Incluso si se permiten asignaciones de estructuras, pasar una estructura como argumento puede no ser muy buena idea en ocasiones; por ejemplo:

#define LINEAS 24

#define COLUMNAS 80

struct ventana { /* una parte de una pantalla */

char lineas [LINEAS] [COLUMNAS];

int ult_col; /* esquina superior */

int ult_fila; /* esquina superior */

int nlineas; /* esquina superior */

int ncols; /* esquina superior */


La llamada a "muestra" (definida en otro lugar) copia la totalidad de la estructura en el stack. Esta estructura contiene LINEAS * COLUMNAS = 24 x80 = 1920 caracteres, así como algunas variables enteras. Compárese esto frente a la alternativa de pasar como argumento un puntero a la estructura, que requeriría tan sólo una variable entera (la dirección de la estructura).

Hay que destacar que pantalla.lineas es un array, y aquí se pasaría un puntero al primer elemento de este araay en la llamada a la función borra_pantalla.

Los punteros a estructuras se declaran de la manera habitual, como los restantes punteros:

/* extrae elementos de la estructura

# persona y los imprime

*/

ppersona (p)

struct persona *p

{

print-f ( "Nombre: %s\n", p->nombre) ;

print-f ("Dirección: %s\n", p->direccion) ;

print-f ("Código Postal: "%d\n", p->cod_postal)

print-f ("Código de la S.S.:%d\n", p->ss_num)

printf("Sueldo: %.2-f\n", p->sueldo);

printf ("Fecha de nacimiento: %d %d %d\n",

p->nacim_fecha.dia,

p->nacim_fecha.mes,

p->nacim_fecha.año);

print-f ("Fecha de incorporación: %d $d %d\n",

p->incorp_fecha.dia,

p->incorp_fecha.mes

p->incorp_fecha. año);

La construcción

puntero-a-estructura->miembro

es equivalente a:

(*puntero-a-estructura) .miembro

El operador "->" consiste eb un signo menos "-" seguido por el signo mayor ">".
Los paréntesis en "(*p).nombre" son necesarios porque el orden de evaluación del operador "." (miembro de una estructura) es mayor que el del operador "*" (valor apuntado por) Así *emp.nombre es equivalente a emp.nombre[0].
Los operadores "." y "->" tienen mayor prioridad que los operadores aritmèticos. Así pues, dada la declaración:

struct {
int x;
inty;
} *p;
la expresiòn
++p->x

incrementaría el valor del miembro "x" de la estructura apuntada por "p", en lugar de incrementar el puntero "p".
Análogamente:

*p->y; obtiene el valor de "y" apuntado por "p"
*p->y++ idem. , incrementando p->y
(*p->y)++ incrementa la "y" apuntada por "p"
*p++->y; incrementa "p" tras buscar p->y
Del mismo modo que C permite arrays de arrays como un tipo más de variables, también permite la definición de arrays de estructuras.

lunes, 1 de febrero de 2010

Campos

Dentro de la declaraciòn de una estructura puede aparecer un miembro seguido por un caràcter de dos puntos ":" y una expresiòn constante, denominàndose entonces campo (field).

El tipo del miembro debe ser alguno de los tipos enteros (char, int, short, long o unsigned); la expresión constante especifica cuántos bits van a utilizarse para ese miembro.

Los campos de bit son una alternativa a las operaciones explícitas de desplazamiento y máscaras de bits, Consideremos el programa:

#define KEYWORD 01

#define EXTERNAL 02

# define STATIC 04

int flags;

...

flags |= EXTERNAL | STATIC;

...

if (flags & KEYWORD) {

...

Esto mismo se podría también haber escrito como:

struct {

int is_keyword:1;

int is_extern:1;

int is_static:1;

} flags;

...

flags.is_extern = flags.is_static =1;

...

if (flags.is_keyword){

...

Desafortunadamente, el empleo de campos de bit en C tiene algunos inconvenientes:

• en las versiones antiguas de compiladores de C, todos los campos tienen que ser cantidades sin signo;

• no pueden emplearse datos binarios empaquetadas de una máquina a otra, pues el empaquetado es dependiente dé la máquina al no especificarse si la asignación de bits se hace de izquierda a derecha, o de derecha a izquierda;

• los campos de bit no son exactamente variables, en el sentido de que no pueden definirse punteros a campos de bit, ni arrays de campos, aunque, por supuesto, sigue siendo válido definir punteros a estructuras que contengan campos de bit.