apuntadores y estructuras
TRANSCRIPT
"Apuntadores y Estructuras" Programac ión C Alberto Pacheco
[email protected] (29/May/2007 11:58am)
Presentación hecha en ExpoVision, Alberto Pacheco, Ver 1.0.55, MEXICO D.R. © 2012
Abstract:
Revisión del tópico más temido y obscuro de Lenguaje C.. los APUNTADORES. Incluye ejemplos y ejercicios.
Keywords:
Lenguaje C, apuntadores, indirección, arreglos, estructuras, memoria dinámica
Contenido
Mapa Conceptual
A b s t r a c t
1. Características de los Apuntadores
4. Apuntadores: Ventajas y Desventajas
5. Apuntadores: Operaciones Básicas
7. Los Datos en Memoria: sizeof()
8. Aritmética de Apuntadores
9. Apuntadores y Cadenas
10. Estructuras
12. Apuntadores y Arreglos de Estructuras
13. Estructuras: Pseudotipos y Parámetros por Refere
14. Asignación Dinámica de Memoria
15. Apuntadores y Arreglos
17. Apuntadores a Funciones
18. Ejercicios
Apuntador: A continuación se resumen sus principales cualidades
Variable que almacena una dirección de memoria.
Una variable de este tipo "apunta" o hace referencia a uno ó más objetos
Desde su declaración "sabe" el tipo de objetos a que hace referencia.
Tipos de apuntadores: a tipos primitivos, arreglos, apuntadores, estructuras y funciones, (*p).field == p->field
El identificador de un arreglo equivale a un apuntador a la dirección del primer elemento, a+i == &a[i] y *(a+i) == a[i]
Parámetros por referencia: en vez de una copia del valor de un parámetro, con apuntadores pasas la dirección del objeto y por lo tanto, la función puede alterar el objeto "externo".
Apuntador Universal: (void*) declara un apuntador genérico, apunta a cualquier tipo de objeto.
Operadores permitidos: asignar, sumar con contantes, restar, comparar, convertir y manejo de bits.
Operadores PROHIBIDOS: multiplicar, dividir y sumar dos o más apuntadores.
Apuntador NULO: valor reservado, dirección cero, significa "apuntador vacío", definido en <stdlib.h>, NULL == (void *)0
PELIGRO: Un apuntador puede accesar indirectamente el contenido de un objeto.
PRECAUCION: Todo apuntador debe inicializarse antes de usarse.
Beneficios:
Generar elementos bajo demanda, asignación dinámica de memoria
Manipular y recorrer grandes espacios de memoria
Generar estructuras de datos complejas
Parámetros de entrada/salida para funciones, parámetros por referencia
Dificultades:
Programación avanzada, caótica y/o complicada
Programación más susceptible de errores muy difíciles de depurar
Dificultad para leer y comprender código
EJERCICIOS
#define NULO ((void *)0)void main(){ int i=5, j; // Datos int *p=NULO, *q=NULO; // Apuntadores printf("\ni=%d, p=%p, q=%p", i, p, q); p = q = &i; // Apuntar al dato *p = 7; // afectar dato (indirección) printf("\ni=%d, p=%p, q=%p", i, p, q); if ( p == q )// Son iguales?
printf("\np es igual a q"); ++*p // incrementa dato, lee dato printf("\ni=%d",i);// i=8 j = *q++;// lee dato, incrementa dirección printf("\nj=%d, p=%p, q=%p", j, p, q); // j=? if ( p != q ) // Diferentes? printf("\np es diferente a q"); if ( q > p ) // Cuál es mayor? printf("\nq es mayor que p");}
Op Función Ejemplo Explicación
(void *)Convierte entero a dirección
(void *)0 dirección nula
*Para declarar apuntadores
int *p;p es un
apuntador
&Obtener
dirección de..q=&i; q apunta a i
=Asignar
direcciónp=q=&i;
p y q apuntan a i
*Operador
indirección*p=7;
a donde apunta p almacena un 7
++Incremento-
dato++*p;
incrementa el valor que apunta p
++ Incremento- *p++; incrementa
apuntador apuntador p
%pEspecificador
tipo apuntadorprintf("%p",p);
imprime dirección
almacenada en p
==igualdad entre apuntadores
p==qregresa falso si no son iguales
!=desigualdad
entre apuntadores
p!=qregresa falso si
son iguales
<menor, <=
menor o igualp<=q
regresa falso si p es mayor que
q
>mayor, >=
mayor o igualp>=q
regresa falso si p es menor que
q
sizeofEspacio que
ocupa un dato en bytes
sizeof(void *)tamaño de cualquier apuntador
#define TYPES 8void main(){ char *tipo[TYPES] = { "char", "short", "int", "long", "float", "double", "long double", "void *" }; int i, tam[TYPES] = { sizeof(char), sizeof(short), sizeof(int), sizeof(long), sizeof(float), sizeof(double), sizeof(long double), sizeof(void *) }; printf("| Tipo-Dato | Bytes |\n"); printf("|-------------|-------|\n"); for (i=0; i<TYPES; i++) printf("| %11s | %2d b. |\n", tipo[i], tam[i]);
printf("|-------------|-------|\n");}
/////////////
main(){ char x[] = "01234567"; char *p = NULL; short *q = (short *)x; int *r = (int *)x; int SIZE = 0; printf("\n\"%s\"={",x); for ( p=x; *p; ++p ) printf("0x%x,",*p); SIZE = p - x + 1; printf("0}\nSize = %d bytes\n",SIZE); for ( p=x; (char *)r-x+sizeof(int)<SIZE; ++p, ++q, ++r ) { printf("\n char[%p]=%x",p,*p); printf("\nshort[%p]=%x",q,*q); printf("\n int[%p]=%x",r,*r);
printf("\n---------------------------\n"); } getchar();}
Notas:
"01234567"={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0}Size = 9 byteschar[0022FF60]=30short[0022FF60]=3130int[0022FF60]=33323130---------------------------char[0022FF61]=31short[0022FF62]=3332int[0022FF64]=37363534---------------------------
int sc( char *s, char *t ){ if ( !s || !t ) return 0; for ( ; *s && *t; s++, t++ ) if ( *s != *t ) break; return *s == *t;}void asg( char *d, char *f ){ while ( *d++ = *f++ );}void idem( char *a, char *b )
{ printf("\n%s! '%s'-'%s'\n",(sc(a,b)?"Iguales":"Diferentes"),a,b);}
main(){ char p[20]="Hola", q[20]="Adios"; idem(p,q); asg(p,q); idem(p,q); getchar();}
Estructura: Permite definir un tipo-derivado que asemeja un "registro" compuesto por campos/miembros heterogeneos. Para acceder cada miembro de una variable tipo estructura se usa el operador-punto. Puede usarse una lista de inicialización para inicializar una estructura.
struct tipo {char member_1;int member_2;};struct tipo x, y={'U',2};x.member_1 = 'A';
Pseudotipos (typedef): Es un alias para declarar variables de tipo struct.
Apuntadores a estructuras: Se usa la notación p->member
Arreglos de estructuras:
struct tipo x[8];
typedef struct {char member_1;int member_2;} s_type;s_type x={'U',2}, *p = &x;p->member_1 = 'R'; // Equivale a (*p).member_1 = 'R';
struct Alumno { char *nombre; unsigned calif;};void main(){ struct Alumno *p, grupo[] = { {"Juan", 73}, {"Ana", 86}, {"Luis", 80}, {(void *)0, 0} }; printf("|--------------|\n"); for (p=grupo; p->nombre; p++) printf("| %6s | %3d |\n", p->nombre, p->calif); printf("|--------------|\n");}
typedef struct { char *nombre; unsigned calif;} Alumno;unsigned mayor_calif(Alumno *p){ unsigned mayor = 0; for (; p->nombre; p++) if ( p->calif > mayor ) mayor = p->calif; return mayor;}void main()
{ Alumno *p, grupo[] = { {"Juan", 73}, {"Ana", 86}, {"Luis", 80}, {NULL, 0} }; printf("|--------------|\n"); for ( p=grupo; p->nombre; p++ ) printf("| %6s | %3d |\n", p->nombre, p->calif); printf("|--------------|\n"); printf("\nla calificación más alta del grupo es: %d", mayor_calif(grupo));}
//////////
#include <stdlib.h> // malloc#include <assert.h> // assertvoid main(){ const int TAM = 10; int i, *p, *q; p = (int *)malloc(TAM*sizeof(int)); assert(p); // Termina si falla asignación de memoria (p==NULL)
for (i=0, q=p; i<TAM; i++, q++ ) { *q = i; printf("\nq=%p, dato=%d", q, *q);
} free(p);}
Ejemplo ilustrativo sobre similitudes y diferencias entre las siguientes declaraciones:
Arreglo 3 reng x 4 col.
Arreglo de apuntadores a
enteros
Apuntador a un arreglo de enteros
Apuntador a apuntadores a
enteros int t[3]
[4]; int *p[3]; int (*q)[4]; int **r;
1 2 3 4 1 2 3 4
1 2 3 4 1 2 3 4
1 2 3 4 ... ...
1 2 3 4
Tabla 3 x 4 Tabla 3 x n Tabla n x 4 Tabla m x n
#include <stdio.h>#define P(s) printf("\n%10s=%d", #s, (s))#define L() printf("\n----------------\n")main(){ int a[6] = { 9,8,7,6,5,4 }; int *p[6] = { a+5, a+4, a+3, a+2, a+1, a }; int (*q)[6] = &a; int **r = p; P(*p[0]); P(**p); L(); P(*p[1]); P(**(p+1)); L();
P((*q)[0]); P(**q); L(); P((*q)[1]); P(*(*q+1)); L(); P(*r[0]); P(**r); L(); P(*r[1]); P(**(r+1)); L();}
///&&&&&&&&
#include <stdio.h>void f1() { printf("\nCorriendo f1()"); }void f2() { printf("\nCorriendo f2()"); }void f3() { printf("\nCorriendo f3()"); }typedef void (*ApFun)(); // KISS main() { void (*p)() = f1; (*p)(); ApFun q = f2; (*f2)(); ApFun a[] = { f1, f2 ,f3, NULL }; int i; for ( i=0; a[i]; i++ ) (*a[i])();}
Indicaciones Generales: Reusando el pseudotipo Alumno defina las siguientes funciones y ejecútelas desde main con datos de ejemplo de 5-10 alumnos.
Problema [+3pts]: Busca en un arreglo p un alumno y regresa su calificación o -1 si no lo encuentra
Prototipo: int busca_alumno(Alumno *p, char *alumno)
Testing: Llamar e imprimir el resultado de dos búsquedas, una exitosa y otra que falle. Problema [+3pts]: Comparar dos arreglos (p,q) y que regrese verdadero si ambos arreglos son idénticos
Prototipo: int compara_alumnos(Alumno *p, Alumno *q)
Testing: Llamar e imprimir el resultado de dos búsquedas, una exitosa y otra que falle. Problema [+4pts]: Copiar de un arreglo (p) todos sólo el conjunto de alumnos cuya calificación sea mayor o igual a calif
Prototipo: Alumno *copiar_aprobados(Alumno *p, unsigned calif)
Testing: Llamar e imprimir el resultado de dos copias, una con calif de 60 y otra con 70.
[C Pointers]
Wikibooks, "Programming:Pointers and Arrays", 2005.http://en.wikibooks.org/wiki/C_Programming/Pointers_and_arrays