Estructuras - UNR · Estructuras Una estructura es una colección de una o más variables, de tipos...

Preview:

Citation preview

Estructuras

Informática II (2012)

Estructuras

Una estructura es una colección de una o más variables, de tipos posiblemente diferentes, agrupadas bajo un solo nombre para manejarlas de forma mas conveniente.

En otros lenguajes se suelen llamar registros. Ayudan a organizar datos complicados, en

particular dentro de programas grandes, debido a que permiten que a un grupo de variables relacionadas se les trate como a una unidad en lugar de como entidades separadas

Ejemplos típicos de estructuras

Un punto manejado por sus coordenadas Un rectángulo manejado por dos puntos

opuestos en diagonal La nómina de una empresa: un empleado

está descripto por un conjunto de atributos tales como: nombre, domicilio, número de CUIL, salario, etc.

Algunos de estos atributos, tales como el domicilio, pueden a su vez ser estructuras

Estructuras

Las estructuras pueden inicializarse, copiarse y asignarse, pasarlas a funciones como argumentos o ser retornadas por ellas.

Conceptos básicos sobre estructuras

Definamos una estructura para graficación.

El objeto básico es un punto que tiene coordenadas x e y, ambas enteras.

Conceptos básicos sobre estructuras

Los dos componentes pueden colocarse en una estructura declarada así:struct punto{int x;int y;

}; La palabra reservada struct presenta la

declaración de una estructura, que es una lista de declaraciones entre llaves.

Conceptos básicos sobre estructuras

Un nombre optativo, llamado rótulo de estructura, puede seguir a la palabra struct (como aquí lo hace punto). El rótulo da nombre a esa clase de estructura y, en adelante, puede usarse como una abreviatura para la parte de declaraciones entre llaves.

Las variables nombradas dentro de la estructura se llaman miembros. Un miembro de estructura o rótulo y una variable ordinaria (es decir, no miembro) pueden tener el mismo nombre sin conflicto, puesto que siempre pueden distinguirse por el contexto. En diferentes estructuras pueden encontrarse los mismos nombres de miembros, aunque por cuestiones de estilo deberían usarse los mismos nombres sólo para objetos estrechamente relacionados.

Conceptos básicos sobre estructuras

Una declaración struct define un tipo. La llave derecha (}) que termina la lista de miembros puede ser seguida de una lista de variables, como se hace con cualquier tipo de dato básico. Por ejemplo,

struct {…}x,y,z; Declara a x, y, z como variables del tipo

nombrado (o listado entre llaves, en el caso de estructuras anónimas) y causa que se les reserve espacio

Conceptos básicos sobre estructuras Una declaración de estructura que no está

seguida por una lista de variables (como en punto) no reserva espacio de almacenamiento sino que simplemente describe una plantilla o la forma de una estructura.

Sin embargo si la declaración está rotulada, el rótulo puede emplearse posteriormente para definir instancias de la misma.

Por ejemplo: struct punto pt; Define una variable pt que es una estructura

de tipo punto

Conceptos básicos sobre estructuras

Una estructura puede inicializarse al seguir su definición con una lista de inicializadores, cada uno es una expresión constante, para cada uno de los miembros:

struct punto maxpt={320,200}; Inicializa la el miembro x de maxpt a 320 y el

miembro y de maxpt a 200 Una estructura local o automática también puede

inicializarse por asignación o llamando a una función que regrese una estructura del tipo adecuado

Conceptos básicos sobre estructuras

Se hace referencia a un miembro de una estructura en particular, en una expresión, con una construcción de la forma: nombre-estructura.miembro

El operador miembro de estructura “.” conecta el nombre de la estructura con el nombre del miembro. Por ejemplo, para imprimir las coordenadas del punto pt:

printf(“%d,%d”, pt.x, pt.y);

Conceptos básicos sobre estructuras

O, para calcular la distancia del origen (0,0) a pt:

double dist;dist=sqrt((double)pt.x * pt.x + (double)pt.y *pt.y);

Las estructuras pueden anidarse. Se puede representar un rectángulo como un

par de puntos ubicados en esquinas diagonalmente opuestas:

Conceptos básicos sobre estructuras

struct rectangulo{struct punto pt1;struct punto pt2;

};

Conceptos básicos sobre estructuras

La estructura rectangulo contiene dos estructuras punto. Si declaramos pantalla como un rectángulo particular:

struct rectangulo pantalla; Entonces pantalla.pt1.x se refiere a la

coordenada x del miembro pt1 de pantalla

Estructuras y funciones

Las únicas operaciones legales sobre una estructura son: copia o asignación como una unidad, toma de su dirección con & o acceso a sus miembros.

La copia y la asignación incluyen pasarlas como argumento a funciones y retornar valores de funciones.

Las estructuras no pueden compararse

Estructuras y funciones

Hay por lo menos tres acercamientos posibles al usar funciones que manipulan estructuras: pasar separadamente los componentes (miembros), pasar una estructura completa o pasar un puntero a ella. Cada uno tiene sus puntos buenos y malos.

Estructuras y funciones

La primera función generapunto, toma 2 enteros y retorna una estructura punto:

/*generapunto: crea un punto con los miembros inicializados con x e y */struct punto generapunto (int x, int y){

struct punto temp; temp.x=x; temp.y=y; return temp;}

Estructuras y funciones

Nótese que no hay conflictos entre los nombres de argumentos y los miembros con los mismos nombres; incluso la reutilización de los nombres refuerza el vínculo.

Ahora esta función puede usarse para inicializar dinámicamente cualquier estructura de tipo punto o para proporcionar argumentos del tipo de la estructura a una función:

Estructuras y funciones

struct rectangulo pantalla;

struct punto medio;//inicializa los miembros del rectángulo pantallapantalla.pt1=generapunto(0,0);pantalla.pt2=generapunto(XMAX, YMAX);

//genera el punto medio de la diagonal entre pt1 y//pt2medio=generapunto((pantalla.pt1.x+pantalla.pt2.x)/2,

(pantalla.pt1.y+pantalla.pt2.y)/2);

Estructuras y funciones

El siguiente paso es escribir un conjunto de operaciones para hacer operaciones aritméticas sobre los puntos, por ejemplo:

/*sumapuntos: suma dos puntos*/struct punto sumapuntos(struct punto p1, struct punto p2){

p1.x +=p2.x; p1.y +=p2.y; return p1;

}

Estructuras y funciones

Aquí, tanto los argumentos como el valor de retorno son estructuras. Incrementamos los componentes en p1 en lugar de utilizar explícitamente una variable temporal (de tipo punto), para hacer énfasis en que los parámetros de la estructura son pasados por valor como cualquier otro tipo de parámetro.

Como otro ejemplo, la función enrectangulo prueba si un punto está dentro de un rectángulo, se adopta la convención de que un rectángulo incluye sus lados izquierdo e inferior pero no sus lados superior y derecho:

Estructuras y funciones

/*enrectangulo: regresa 1 si p esta en r, 0 si no lo esta*/int enrectangulo (struct punto p, struct rectangulo r){

return p.x >=r.pt1.x && p.x <r.pt2.x &&

p.y >= r.pt1.y && p.y <r.pt2.y;}

Estructuras y funciones

Esto supone que el rectángulo está representado en una forma estándar en donde las coordenadas pt1 son menores que las coordenadas pt2. La siguiente función regresa un rectángulo garantizado que está en esta forma canónica:

Estructuras y funciones#define min(a,b) ((a)<(b) ? (a): (b))#define max(a,b) ((a)>(b) ? (a): (b))

/*canonrect: pone en forma canonica las coordenadas de un rectangulo*/

struct rectangulo canonrect(struct rectangulo r){ struct rectangulo temp; temp.pt1.x=min(r.pt1.x, r.pt2.x);

temp.pt1.y=min(r.pt1.y, r.pt2.y);temp.pt2.x=max(r.pt1.x, r.pt2.x);temp.pt2.y=max(r.pt1.y, r.pt2.y);return temp:

}

Estructuras y funciones

Si se pasa a una función una estructura grande, generalmente es más eficiente pasar un puntero que copiar la estructura completa. Los punteros a estructuras son como los punteros a variables ordinarias.

La declaración struct punto *pp; Dice que pp es un puntero a una estructura

de tipo punto. Si pp apunta a una estructura punto, *pp es la estructura y (*pp).x y (*pp).y son los miembros

Estructuras y funciones

Para emplear pp, podría escribirse, por ej.:struct punto origen, *pp;pp=&origen;printf(“El origen es (%d,%d)\n”, (*pp).x, (*pp).y);

Los paréntesis son necesarios en (*pp).x debido a que la precedencia del operador miembro de estructura . es mayor que la de *. La expresión *pp.x significa *(pp.x), lo cual es ilegal puesto que x no es un puntero sino que es un entero

Estructuras y funciones

Los punteros a estructuras se usan con tanta frecuencia que se ha proporcionado una notación alternativa como abreviatura. Si p es un puntero a estructura, entonces

p->miembro de estructura Se refiere a un miembro en particular. (El

operador -> es un signo menos seguido inmediatamente por >)

Estructuras y funciones

Así podríamos haber escrito: printf(“El origen es (%d,%d)\n”, pp->x, pp->y);

Tanto . como -> se asocian de izquierda a derecha, de modo que si tenemos:

struct rectangulo r, *rp=&r; Entonces estas 4 expresiones son

equivalentes:

Estructuras y funciones

r.pt1.x;rp->pt1.x;(r.pt1).x;(rp->pt1).x;

Los operadores de estructuras . y -> junto con ( ) para llamadas a funciones y [ ] para subíndices, están arriba en la jerarquía de precedencias y se asocian estrechamente.

Estructuras y funciones

Por ejemplo, dada la declaración:struct{int len;char *str;

}*p; entonces ++p->len; Incrementa a len, no a p, puesto que los

paréntesis implícitos son ++(p->len);

Estructuras y funciones

Los paréntesis pueden emplearse para alterar la asociación: (++p)->len; incrementa a p antes de tener acceso a len, y (p++)->len; incrementa a p después del acceso a len (este último conjunto de paréntesis es innecesario)

De la misma manera *p->str; obtiene cualquier cosa a la que str esté apuntando, *p->str++; incrementa a str después de hacer el acceso a lo que apunta (exactamente como *s++); (*p->str)++; incrementa cualquier cosa a la que str apunte y *p++->str incrementa a p después de hacer el acceso a lo que str apunta

Arreglos de estructuras

En el siguiente ejemplo se observa como se declara un arreglo de estructuras, definidas de manera tal que, mantienen la información necesaria para describir un intervalo numérico y, la cantidad de veces que un número generado al azar cae en ese intervalo, como para armar un histograma.

(Ref.: http://www.ib.cnea.gov.ar/~servos/CursoC/estructuras.htm)

Arreglos de estructuras#include <stdlib.h>

#include <stdio.h>

#define NUM_INTER 100

struct Intervalo

{

/* Límite inferior del intervalo */

double Lower,

Upper; /* Límite superior del intervalo */

int Counter; /* Cantidad de puntos dentro de él*/

};

Arreglos de estructuras

main()

{

struct Intervalo Inter[NUM_INTER];

int Index;

//Delta: ancho intervalo

double Delta = RAND_MAX/NUM_INTER;

//RAND_MAX 32767, mayor nro.de rand()

/* Inicialización del vector de estructuras */

Arreglos de estructuras

for (Index=0; Index<NUMINTER; Index ++)

{

Inter[Index].Counter = 0;

Inter[Index].Lower = Delta*Index;

Inter[Index].Upper = Delta*(Index+1);

}

Arreglos de estructuras//genera 10000 nros. al azar

for (Index=0; Index<10000; Index++)

//incrementa contador del intervalo donde cae el nro.

//cuyo índice es determinado por:rand()/Delta

Inter[rand()/Delta].Counter++;

//rand() seudoaleatorio entre 0 y 32767

/* impresión de resultados */

for (Index=0; Index<NUM_INTER; Index++)

printf("[%lf,%lf]=%d/n", Inter[Index].Lower,

Inter[Index].Upper, Inter[Index].Counter);

}

Arreglos de estructuras

struct Item{                  char     material[50] ;int        existencia ; double costo_unitario ;

};//inicializo como cualquier arrayItem stock1[3] = {//tamaño stock1:(sizeof(stock1)/sizeof(struct Item))//o (sizeof(stock1)/sizeof(stock1[0]), mejor "tornillos" , 120 , .15 ,

"tuercas" , 200 , .09 ,

"arandelas" , 90 , .01

} ;

Estructuras

No hay que suponer que el tamaño de una estructura es la suma de los tamaños de sus miembros. Debido a requisitos de alineación para diferentes objetos, podría haber “huecos” no identificados dentro de la estructura

El operador sizeof regresa el valor apropiado

Estructuras

Punteros a estructuras

http://c.learncodethehardway.org/book/learn-c-the-hard-waych17.html#include <stdio.h>#include <stdlib.h>#include <string.h>struct Persona{

char *nombre;int edad;int altura;int peso;

};struct Persona *creaPersona(char *name, int age, int height, int weight){ struct Persona *who = (Persona*) malloc(sizeof(struct Persona)); if(who != NULL){ who->nombre = strdup(name);//crea duplicado, hay que liberar memoria who->edad = age; who->altura = height; who->peso = weight;

} return who;}

Punteros a estructuras

void destruyePersona(struct Persona *who) { if(who != NULL){//libera memoria free(who->nombre);//en este orden la destrucción free(who); }}void imprimePersona(struct Persona *who){ printf("Nombre: %s\n", who->nombre); printf("\tEdad: %d\n", who->edad); printf("\tAltura: %d\n", who->altura); printf("\tPeso: %d\n", who->peso);}

Punteros a estructuras

int main(void){

//crea dos personasstruct Persona *joe = creaPersona("Joe Alex", 32, 64, 140);struct Persona *frank =creaPersona("Frank Blank",20,72,180);

//imprime y muestra donde están en memoria

printf("Joe esta en la direccion de memoria %p:\n", joe);imprimePersona(joe);printf("Frank esta en la direccion de memoria %p:\n",frank);imprimePersona(frank);

Punteros a estructuras

//hace a las personas 20 años mayores e imprime nuevamente sus datos

joe->edad += 20; joe->altura -= 2; joe->peso += 40; imprimePersona(joe); frank->edad += 20; frank->peso += 20; imprimePersona(frank); //libera espacio de memoria destruyePersona(joe); destruyePersona(frank); return 0;}

Typedef

C proporciona una facilidad llamada typedef (especificador de categoría de almacenamiento) para crear nuevos tipos de datos. Ejemplo:

typedef int Longitud; Hace del nombre Longitud un sinónimo de int El tipo Longitud puede emplearse en

declaraciones, casts, etc., exactamente de la misma forma en que lo podría hacer int

Longitud len, maxlen; Longitud *lengths[ ];

Typedef

De modo semejante, la declaración: typedef char *Cadena; Hace a Cadena un sinónimo para char* o puntero a

carácter Cadena p, lineptr[MAXLINES], alloc(int); int strcmp(Cadena, Cadena);//prototipo p=(Cadena)malloc(100); El tipo declarado en un typedef aparece en la

posición del nombre de variable, no justo después de la palabra typedef

Normalmente se usan los nombres con mayúsculas para destacarlos

Typedef

Una declaración typedef no crea un nuevo tipo en ningún sentido; simplemente agrega un nuevo nombre para algún tipo ya existente

Las variables declaradas de esta manera tienen exactamente las mismas propiedades que las variables cuyas declaraciones se escriben explícitamente

Typedef

Además de razones puramente estéticas, hay dos motivos principales para emplear typedef:

La primera es, parametrizar un programa contra los problemas de transportabilidad. Si se emplea typedef para tipos de datos que pueden ser dependientes de la máquina, cuando un programa se traslada, sólo los typedef requieren de cambios.

Typedef

Un segundo propósito es proporcionar mejor documentación del programa

Uso común con enumeraciones: typedef enum {FALSE=0, TRUE} Boolean; Boolean flag = TRUE; Con estructuras:

typedef struct telephone{

char *name;int number;

}TELEPHONE; TELEPHONE index; typedef TELEPHONE *pf; //puntero a la estructura TELEPHONE

Uniones

Una unión es una variable que puede contener (en momentos diferentes) objetos de diferentes tipos y tamaños y, el compilador hace el seguimiento del tamaño y requisitos de alineación

Las uniones proporcionan una forma de manipular diferentes clases de datos dentro de una sola área de almacenamiento, sin incluir en el programa ninguna información dependiente de la máquina

Uniones#include <stdio.h>

union marks { float perc;//nota en % char grade; //nota en letras };int main ( ) { union marks student1; student1.perc = 98.5; //dirección como unsigned long,alineado der.con ancho 16 printf(" La nota es: %f, la direccion es: %16lu\n",

student1.perc, &student1.perc); student1.grade = 'A'; printf("En letras: %c, la direccion es: %16lu\n",

student1.grade, &student1.grade); return 0; }

Uniones

La variable marks será suficientemente grande como para mantener al mayor de los tres tipos: el tamaño específico depende de la implantación.

Cualquiera de los tipos de los miembros puede asignarse, por ejemplo a student1 y luego emplearse en expresiones, mientras que el uso sea consistente

Uniones

Sintácticamente se tiene acceso a miembros de una unión como:

Nombre-union.miembro o, Puntero-union->miembro Al igual que en las estructuras Las operaciones que pueden realizarse con

uniones: Asignar una unión a otra del mismo tipo, tomar la dirección (&) de la unión, acceder a un miembro

Uniones

Las uniones pueden presentarse dentro de estructuras y arreglos y viceversa.

La notación para tener acceso a un miembro de una unión en una estructura (o viceversa) es idéntica a la de estructuras anidadas

Una unión sólo puede inicializarse en la declaración con un valor del tipo del primer campo o miembro, en el ejemplo anterior, con un float

Uniones Muy usadas en la programación de sistemas

embebidos o en situaciones donde sea necesario acceder al hardware/memoria directamente

typedef union{ struct { unsigned char byte1; unsigned char byte2; unsigned char byte3; unsigned char byte4; } bytes; unsigned int dword;} HW_Register;HW_Register reg;

Uniones

E intentar acceder a reg:reg.dword = 0x12345678;reg.bytes.byte3 = 4;

Uniones http://www.freescale.com/files/soft_dev_tools/doc/white_paper/CWPORTINGWP.pdf

Uniones

http://cprogramminglanguage.net/c-union.aspx

#include <stdio.h>

enum account_type{personal = 1, business = 2}; /* person name */struct person{ char* name;};

/* company with name and tax no*/struct company{ char* name; char* tax_no;};

Unionestypedef union{ struct person individual; struct company biz;} profile;

typedef struct{ char* username; char* password; enum account_type type; profile info;} account; void display(account acc);

Unionesint main(){ printf("Union Demo\n"); account acc1, acc2; acc1.type = personal; acc1.username = "acc1"; acc1.password = "secret"; acc1.info.individual.name = "John Doe"; display(acc1); acc2.type = business; acc2.username = "acc2"; acc2.password = "secret2"; acc2.info.biz.name = "My Company"; acc2.info.biz.tax_no = "112121"; display(acc2); return 0;}

Uniones/* displays account on screen*/void display(account acc){ switch(acc.type) { case personal: printf("Personal Account\n");

printf("username:%s\nname:%s\n",acc.username,acc.info.individual.name);

break; case business: printf("Business Account\n");

printf("username:%s\ncompany:%s\ntax no.:%s\n",acc.username,acc.info.biz.name,acc.info.biz.tax_no);

break; } printf("-------------------------------\n");}

Con punteros y estructuras…#include <stdio.h>#define PI 3.141592struct square{ int side;};struct circle{ int radius;};struct rectangle{ int side1; int side2;};union shape_union{ struct rectangle *rect; struct square *squa; struct circle *circ;};enum shape_type { RECTANGLE, SQUARE, CIRCLE };struct shape_struct{ union shape_union shape_u; shape_type st;};typedef struct shape_struct shape;

Con punteros y estructuras…double area(shape *s){

if(s == NULL)

return -1;

switch(s->st) {

case RECTANGLE:

return (s->shape_u.rect->side1) * (s->shape_u.rect->side2);

case SQUARE:

return (s->shape_u.squa->side) * (s->shape_u.squa->side);

case CIRCLE:

return (s->shape_u.circ->radius) * (s->shape_u.circ->radius)* PI;

default: /* error */

return -1; }}

Con punteros y estructuras…int main(){

struct circle c,*pc;

c.radius=1.0;

shape_type mitipo=CIRCLE;

pc=&c;

union shape_union su;

su.circ=pc;

shape s,*ps;

s.shape_u=su;

s.st=mitipo;

ps=&s;

printf("El area del circulo unitario es: %lf\n",area(ps));

return 0;}

Campos de bits

Cuando el espacio de almacenamiento es escaso, puede ser necesario empaquetar varios objetos en una única palabra de máquina

Se utilizan como estructura de datos (con formatos impuestos externamente), normalmente, como interfaz con dispositivos hardware: tarjetas, controladores, puertos de comunicaciones, etc.

Campos de bits

C ofrece la posibilidad de definir y acceder a bits individuales (o en grupos) dentro de una palabra de máquina

Un campo de bits es un conjunto de de bits adyacentes dentro de la unidad de almacenamiento (palabra)

struct Puertos{ unsigned Ch0: 1; // Rango 0,1 unsigned Ch1: 2; // Rango 0 a 3

unsigned Ch2: 3; // Rango 0 a 7 unsigned Ch3: 1; // Rango 0,1 };

Campos de bits

Se define en el ejemplo una tabla de variables llamada Puertos que contiene 4 campos de bits

Los números que siguen a :, representan el ancho del miembro en bits

Los miembros o campos individuales se referencian de la misma forma que en el caso de cualquier otra estructura o unión:

Campos de bits

struct Puertos PU_1; PU_1.Ch0 = 0; PU_1.Ch1 = 3; PU_1.Ch2 = 5;

PU_1.Ch3 = 1; Los campos se comportan como enteros

pequeños y pueden participar en expresiones aritméticas como cualquier entero

Campos de bits

Contenido en memoria de la estructura de campos de bits

Campos de bits

Casi todo lo referente a campos de bits es dependiente de la implementación

Los campos no necesitan tener nombres, en caso de campos anónimos (sólo figuran los : seguido del ancho del campo de bits) se utilizan como relleno

El ancho especial 0 se utiliza para forzar alineamiento con el límite de la siguiente palabra

Campos de bits

Los campos sólo pueden declararse como enteros (por razones de portabilidad), debe definirse explícitamente si son signed o unsigned

No son arrays, no tienen direcciones así que no se les puede aplicar el operador &

En algunas plataformas los campos son asignados de izquierda a derecha y en otras,al revés

Campos de bits

O sea, aunque son útiles para mantener estructuras de datos definidas internamente, este tema tiene que ser considerado cuidadosamente cuando se utilizan para almacenar datos con formatos impuestos externamente

Los programas que se basan en tales consideraciones no son portables

Campos de bits

Campos de bits

Si a veces quiero acceder a los campos de bits individuales y a veces al registro completo:

Dos formas de leer la misma posición de memoria

Campos de bits

Campos de bits

Muy usados para datos en zonas especiales del espacio de direcciones de memoria dedicados a los registros de control del hardware (entrada y salida mapeada en memoria)

Para acceder a estos registros uso punteros. Si el Port D Data Register (PTD) está en la dirección de memoria 0x03, podría definir el puntero:

volatile char *PTD = (char*)0x03; Se declara volatile porque su valor puede cambiar en

cualquier momento aún por causas externas al programa (interrupciones)

El cast es necesario para evitar errores de compilación

Campos de bits

También el registro podría manejarse usando campos de bits

Recommended