El lenguaje C proporciona cinco maneras diferentes de crear tipos de datos, éstos son:

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 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 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 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 #include 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 #include 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 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 ta

12 downloads 30 Views 29KB Size

Story Transcript

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 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 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 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 #include 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 #include 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 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 #include 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

Get in touch

Social

© Copyright 2013 - 2024 MYDOKUMENT.COM - All rights reserved.