Hp04 Estructuras y Punteros

Anuncio
CAPÍTULO 4
ESTRUCTURAS, PUNTEROS Y
ASIGNACIÓN DINAMICA DE MEMORIA
4.1. TIPOS DE DATOS
El lenguaje C proporciona cinco maneras diferentes de crear tipos de datos, éstos son:
•
•
•
•
•
Estructuras
Campos de bits
Uniones
Enumeraciones (ANSI)
typedef
Estructuras
Las estructuras son un conjunto de datos agrupados que forman una entidad lógica. También se dice que
son una agrupación de variables bajo un mismo nombre (o que se referencia bajo un mismo nombre).
El formato general es el siguiente:
struct tipo_estructura{
tipo nombre_var;
...
tipo nombre_var;
} variable_estructura;
Ej 4.1. Definiendo un estructura llamada fecha.
struct fecha {
int dia;
int mes;
int a;
} d;
/*
Elementos de la estructura
/*
Nombre de la variable
*/
*/
/* fecha es el nombre de la estructura */
Este no es el único formato, existen otros más simples, los cuales están indicados en la tabla 4.1.
34
Preparado por Juan Ignacio Huircán
Formato
Descripción
struct fecha {...};
Se define el nombre de la estructura fecha y la descripción, pero no se ha
definido ninguna variable.
struct {....} j;
No se le ha dado nombre a la estructura, pero si se ha definido una variable j la
cual tiene una descripción que está definida entre { y }.
struct X s;
Se declara una variable llamada s, cuya definición del contenido es X. (El que fue
o debe ser previamente definido).
struct X a,b,c;
Se declaran 3 variables, todas con la estructura X
Tabla 4.1. Otras formas de definir una estructura.
Se pueden realizar operaciones con los miembros de las estructuras y no con la estructura en su conjunto.
La única operación que se puede hacer es & (cálculo de dirección). Al aplicárselo a la estructura devuelve la
dirección del primer byte (octeto) que ocupa la estructura.
Para referenciar los elementos individuales de la estructura, se usa el operador punto (.).
variable_estructura.nombre_elemento
Ej 4.3. Accesando los elementos de una estructura.
#include <string.h>
void main()
{
struct direccion{
char calle[25];
int numero;
char nombre[30];
} d;
strcpy(d.calle,"Avd. Alemania");
d.numero=2010;
strcpy(d.nombre,"Fulano");
}
Ej 4.4. Se pueden realizar operaciones si los elementos son de tipo numérico.
void main()
{
struct complex { float x; float y;} x;
float modulo;
x.x=0.5;
x.y=10.0;
modulo=sqr(x.x*x.x+x.y*x.y);
}
Herramientas de Programación
35
En el caso de ser requerido pueden definirse matrices o arrays, es decir:
struct direccion dir[100];
Para acceder a ella se puede hacer de la siguiente forma:
dir[1].numero= 1002;
Punteros a estructuras
Si se desea acceder a un miembro concreto de la estructura debe utilizarse un puntero de dirección. El
formato para acceder es el siguiente:
nombre_puntero->nombre_elemento
Ej 4.5. Declaración de un puntero a estructura.
struct direccion{
char calle[25];
int numero;
char nombre[30];
} d;
void main()
{
struct direccion *p;
/* puntero a estructura */
.....
} /* En este caso no hay ninguna variable definida */
Ej 4.6. Forma de acceder a una estructura mediante un puntero.
#include<string.h>
struct direccion {
char calle[25];
int numero;
char nombre[30];
};
void main()
{
struct direccion d, *p; /* se declara d */
p=&d;
/* p contiene la direccion del primer byte de d */
strcpy(p->calle,"Avd. Alemania");
p->numero=2010;
strcpy(p->nombre,"Fulano");
.....
}
36
Preparado por Juan Ignacio Huircán
Estructuras de bits
El lenguaje C permite manipular y definir campos de longitud inferior a un byte. O sea, permite acceder a
un bits individual dentro de un byte.
Si el almacenamiento es limitado (RAM), se pueden definir y almacenar varias variables booleanas dentro
de un byte. Algunos periféricos devuelven la información codificada dentro de un byte.
La forma general de definir estos campos de bits es:
struct nombre_estructura {
tipo nombre1: longitud;
tipo nombre2: longitud;
tipo nombre3: longitud;
...
};
Los campos de bits, cuando son más de uno, tienen que declararse como int, signed o unsigned . Si
los campos de bits son de longitud uno, debe declararse como unsigned (Obvio!).
Ej 4.7. Definición de una variable en la que se especifícan los campos de bits.
#include<stdio.h>
void main()
{
struct uart_reg {
unsigned cardis:1;
unsigned overrun:1;
unsigned errorpar:1;
} uart_st;
if(uart_st.cardis)printf("Llegó
dato..!");
}
El conjunto de estos bits se trata como palabra de microprocesador. Si es IBM PC es de 16 bits. La
estructura anterior se extiende a 16 bits, rellenándose por la izquierda.
Las Uniones
En C una union es una posición de memoria que es utilizada por diferentes variables, la cuales pueden
tener diferente tipo. La definición de una union en código es similar a una estructura.
union
tipo_union{
int i;
char c;
} u;
Cuando se declara una union el compilador crea automáticamente una variables suficientemente grande
para guardar el tipo más grande de variable de la union. Para acceder a las uniones se utiliza la misma sintaxis
que se utiliza para las estructuras.
u.i=120;
Otro formato puede ser el siguiente:
Herramientas de Programación
37
union{
int i;
char c[2];
}u_var;
/* U: nombre de la union */
/* Puede considerarse como un int o */
/* dos bytes */
/* Nombre de la variable */
Según el momento de ejecución del programa, esa zona de memoria pude considerarse como entero o una cadena
de caracteres o un campo de 16 bits, etc..
Ej 4.8. Declaración de una union.
union {
int i;
char c[2];
} u;
u.i=263;
/* 263 10 = 00000001 0000 0111 2 = 0107h */
Para este caso los valores para c[0] y c[1] son:
u.c[0]=7;
u.c[1]=1;
Enumeraciones
El estandar ANSI incorporó los tipos enumerados como extensión del C. Se define como el conjunto de
constantes enteras con nombre que especifica todos los valores válidos que una variable de ese tipo puede tomar.
Un ejemplo en TurboC es la asignación de los colores (en modo gráfico) a una variable enum.
Ej 4.9. Definición de enumeraciones.
enum COLORS {
BLACK, BLUE,GREEN, CYAN,RED,
MAGENTA,BROWN, LIGHTGRAY,DARKGRAY,
LIGHTBLUE, LIGHTGREEN, LIGHTCYAN,
LIGHTRED, LIGHTMAGENTA, YELLOW, WHITE
};
Para este caso BLACK es equivalente a decir 0 y WHITE es igual a decir 15.
38
Preparado por Juan Ignacio Huircán
Ej 4.10. Otra forma.
void main()
{
enum color { rojo, azul, verde=4, amarillo};
enum color x, *px;
x=rojo;
/* x=0 */
*px=amarillo;
/* *px=5 */
}
typedef
El C permite al programador definir explícitamente nuevos nombre para los tipos de datos usando la
palabra clave typedef. Realmente no se crea un nuevo tipo de datos, sino que se define un nuevo nombre para un
tipo ya existente.
Ej 4.11. Utilización del typedef.
void main()
{
typedef struct{
char nombre[20];
int edad;
} persona;
persona p[100]; /* Declaración de una matriz de 100 personas */
}
Herramientas de Programación
39
4.2. ASIGNACIÓN DE MEMORIA DINÁMICA
Recordando la estructura dirección , declarada el apartado anterior, podríamos definirnos una matriz de
1000 elementos, sin embargo, estaríamos definiendo demasiadas variables dentro de la zona de datos. Si esta es
una zona limitada, tendremos problemas.
Suponiendo que tenemos un limite de 64Kb para datos:
void main()
{
struct direcciones dir[1000];
int a[20000];
....
}
Para solucionar este problema podemos usar memoria dinámica. Para este efecto debemos saber cuanta
memoria en bytes requieren nuestros datos, entonces, debemos determinar el largo de la estructura en bytes. Para
este propósito se utiliza la función sizeof.
Ej 4.12. Reservando memoria dinámica.
#include<string.h>
#include<alloc.h>
void main()
{
struct direcciones d, *pdir, *pdiri;
int largoreg;
largoreg=sizeof(d);
pdiri=pdir=(struct direcc iones *)farmalloc(1000*largoreg);
strcpy(pdir->calle,"Avd. Alemania");
pdir->numero=2010;
strcpy(pdir->nombre,"Fulano");
pdir++;
/* para accesar el segundo dato */
}
4.3. PASO DE ESTRUCTURAS A FUNCIONES
El paso de estructuras a funciones se puede realizar pasando un puntero asociado a la dirección de dicha
estructura, el tratamiento es similar a paso de argumentos por referencia.
Ej 4.13. El siguiente programa permite el paso de las variables z y z2, definidas en una estructura llamada
complejo , a una función llamada suma, la que realiza dicha operación con dos números complejos devolviendo
el resultado en z.
40
Preparado por Juan Ignacio Huircán
#include<conio.h>
#include<stdio.h>
struct complejo {
float x;
float y;
};
/* Definición de un complejo */
/* real */
/* imag */
void suma(struct complejo *z1, struct complejo *z2);
void main()
{
struct complejo z, z2;
/* Definiendo z, z2 "complejos" */
z.x=1.0;z.y=2.0;
z2.x=3.0;z2.y=3.0;
suma(&z,&z2);
/* Deuelve la suma en z */
printf("%f
+j %f", z.x, z.y);
getch();
}
void suma(struct complejo *z1, struct complejo *z2)
{
(*z1).x=(*z1).x+( * z2).x;
(*z1).y=(*z1).y+(*z2).y; /* Retorna el resultado en z1 */
}
Finalmente muestra el resultado en pantalla:
4.000000 + j 5.000000
Herramientas de Programación
41
4.4. FUNCIONES Y PUNTEROS
El uso de punteros y funciones está bastante relacionado, por un lado tenemos el paso de parámetros por
referencia, cuando se utiliza alguna función, y por otro la llamada de funciones usando punteros.
Punteros a funciones
Una característica (un poco confusa) muy útil del lenguaje C es el puntero a función. La confusión surge
porque una función tiene una dirección física en memoria que puede asignarse a un puntero, aunque la función no
es una variable. La dirección de la función es el punto de entrada de la función; por lo tanto el puntero a función
puede utilizarse para llamar dicha función. Básicamente un puntero a función es una dirección, usualmente un
segmento de código, donde el código ejecutable de la función está almacenado.
Un puntero a función se declara de la siguiente forma
tipo (*nombre_puntero_funcion)();
Donde tipo es el tipo retornado por la función.
Ej 4.14. Formas de declaración de punteros a función.
void (*func)();
/* No recibe argumentos y no retorna nada */
void (*func)(int); /* No retorna nada pero toma un argumento entero*/
La dirección de la función se obtiene utilizando el nombre de la función sin paréntesis ni argumentos
(similar a como se obtiene la dirección de un array).
Ej 4.15. Utilizando un puntero a función.
#include <stdio.h>
void funcion(void);
void (*f)();
void main()
{
f=funcion;
(*f)();
}
/*
Ejecuta funcion() */
void funcion()
{
printf("Funcion \n");
}
42
Preparado por Juan Ignacio Huircán
Los punteros a funciones son muy cómodos cuando se utilizan tablas de decisión. Veamos el siguiente
ejemplo:
Ej 4.16. Utilizando punteros a funciones.
#include <stdio.h>
#include <conio.h>
int
int
int
int
f1(void);
f2(void);
f3(void);
f4(int j);
void main()
{
int (*g)(int);
/* puntero a funcion */
struct {
int (*f)();
} mpf[3]={ {f1}, {f2}, {f3} };
int i=2;
g=f4;
i=(*g)(i);
(*mpf[i].f)();
/* se llama f4 con argumento 2 */
/* llama a la funcion f3 */
}
int f1(void)
{
printf("Funcion 1\n");
getch();
return(1);
}
int f2(void)
{
printf("Funcion 2\n");
getch();
return(2);
}
int f3(void)
{
printf("Funcion 3\n");
getch();
return(3);
}
int f4(int j)
{
printf("Función 4\n");
return(j);
}
Herramientas de Programación
43
Descargar