Beruflich Dokumente
Kultur Dokumente
APUNTADORES, repaso.
Un apuntador, o puntero, es un identificador de programa, ya sea:
poseen distinto sentido: la variable Peso puede ser un operando comn en una expresin de clculo, en tanto que Fuerza indica una direccin (1250) donde presumiblemente se halla el dato que podra ser utilizado en una expresin clculo. Puesto de otra manera estos mismos datos, podramos entender mejor estos conceptos:
int int*
Peso
Direccin Indireccin
La direccin es el contenido en s del identificador, en este caso el valor 14500. La indireccin es aquello que se halla referenciado o almacenado en dicha direccin, en este caso la magnitud 1250.
N1 Pg 1/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti Asignacin a travs de operadores o funciones. Apuntador = &Identificador
(operador de direccin amphersand)
Para el ejemplo anterior tendramos: pPeso = &Peso, donde el operador & retorna la direccin de su operando: en este caso la direccin de la variable Peso. Es de destacar que este operador devuelve una direccin del mismo tipo del operando sobre el cual acta: en este caso retorna un puntero a int.
Las Indirecciones.
La palabra indireccin (o indirection en ings) podramos pensarla ms bien como:
*pPeso = 2500
Est diciendo: en la direccin apuntada por el puntero pPeso, almacene la magnitud 2500. IMPORTANTE: Note bien la diferencia entre pPeso=. y *pPeso= En el primer caso se asigna una DIRECCION a la variable en s, y en el segundo caso se asigna UN VALOR en la direccin apuntada por la variable. El dato almacenado en la direccin pPeso (o del puntero que se tratase) goza de todas las propiedades para ese tipo de dato, en este caso un int (entero):
Cociente de una Divisin Entera. Resto de una Divisin Entera. Puede utilizarse como operando en expresiones aritmticas. Etc.
Ejemplos:
N1 Pg 2/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti
Considerar el siguiente esquema: p1ENT int * &ENT
128
ENT
p2ENT
int *
cuyo cdigo vienen dado a continuacin: #include<conio.h> void main() { int *p1ENT; int *p2ENT; int ENT = 128; clrscr(); highvideo(); p1ENT =&ENT; p2ENT =p1ENT; if(p1ENT==p2ENT) { cprintf("DIRECCIONES APUNTADAS IGUALES\r\n"); getch(); } if(*p1ENT==*p2ENT) { cprintf("CONTENIDOS REFERENCIADOS IGUALES\r\n"); getch(); } } La direccin asignada al apuntador p1ENT se obtuvo mediante el operador &, en tanto que la direccin de p2ENT fue tomada directamente del puntero p1ENT. En la lnea: if(p1ENT==p2ENT), estamos preguntando si el contenido intrnseco de la variable p1ENT es igual al de la variable p2ENT, o lo que es lo mismo, si la direccin apuntada por p1ENT es idntica a la apuntada por p2ENT. En cambio en la instruccin: if(*p1ENT==*p2ENT) averiguamos por el contenido de las direcciones apuntadas por p1ENT y p2ENT. Hay una diferencia neta con el caso anterior: aqu estamos comparando dos enteros y anteriormente comparbamos dos direcciones.
N1 Pg 3/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti
Aritmtica de punteros (++ / -- / + = k / etc.) Tipificados Referencia datos especficos (int * / char * / etc.) Permite operaciones aritmticas (operandos) PUNTEROS No-Tipificados (void *) Direcciones puras (reservas dinmicas) No permiten aritmtica de punteros. 65536 direcciones Cortos (2 bytes) Tamao 1048576 direcciones Largos (4 bytes) Compact / Huge etc. Tinny / Small
CASTs
Se denomina con este nombre a un rtulo entre parntesis, que se coloca delante de un identificador de programa, normalmente una variable de algn tipo, a fin de forzar al compilador a realizar un cambio hacia otro tipo de dato, por ejemplo:
int Fuerza = 125; int Superf = 63; double Presion; Presion = Fuerza/Superf;
El resultado obtenido sera 1 en lugar de 1.984 debido a que el compilador analiza que a la derecha del signo igual se realizar una operacin donde todos los operandos son magnitu-
N1 Pg 4/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti
des enteras, para lo cual asigna buffers transitorios de ese tipo y ejecuta las operaciones basndose en enteros, en este caso un cociente entero: la parte 0.984 es directamente truncada y se queda con lo que est a la izquierda del signo igual. Qu error se ha cometido por el redondeo? Nada menos que el 49,6% con respecto a lo esperado. Estos errores son muy difciles de detectar y producen serios dolores de cabeza a la hora de depurar. La solucin surge de dos posibles acciones: a) Cambiar el tipo de las variables Fuerza y Superf a double. b) Utilizar casts:
/* --------- Ejemplo de clase Nro 1 ------------------------------------------------------------ */ #include <conio.h> #include <math.h> #include <string.h> typedef unsigned int INT; // ---------------------------------------------------------void main() { INT Direcc[4] = { 0,0,0,0 }; double UnDouble = M_PI; char *UnaCadena = "UNA CADENA CUALQUIERA"; int UnEntero = 15620; long UnLong = 6128635; clrscr(); highvideo(); Direcc[0]=(INT)&UnDouble; Direcc[1]=(INT)UnaCadena; Direcc[2]=(INT)&UnEntero; Direcc[3]=(INT)&UnLong; cprintf(" Un Double = %lf\r\n",*((double *)Direcc[0]) ); cprintf(" Una Cadena = %s\r\n", (char *)Direcc[1] ); cprintf(" Un cprintf(" Un Entero = %d\r\n", Long = %ld\r\n", *((int *)Direcc[2]) ); *((long *)Direcc[3]) );
N1 Pg 5/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti
getch(); } // ----------------------------------------------------------Ntese que la operacin es siempre la misma:
Direcc[ ] = (INT)&Identificador
donde (INT) es un cast que convierte una direccin tipificada en su mdulo y recin es almacenado en el arreglo. Un detalle interesante es que cuando se trata de una cadena no se requiere el operador & debido a que C maneja los arreglos como punteros implcitos llevando en su nombre la direccin de comienzo de la misma.
(double *)Direcc[0]
se trata de un apuntador a double. Si queremos tener acceso a lo que l referencia debemos indireccionar. La notacin completa sera entonces:
*((double *)Direcc[0])
*((double)Direcc[0] [0] apuntador a double
N1 Pg 6/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti
Con el mismo puntero pENT recorra el arreglo incrementando su direccin con el operador (++) para verse forzado a chequear y tomar la direccin de inicio del ultimo domicilio. Obviamente deber ir mostrando por pantalla el contenido (fila a fila).
---------------------------------------------------------------- */
#include <conio.h> #include <stdlib.h> typedef unsigned int INT; const int DIM = 12; const int ESC = 27; void main() { char Buff[24]; INT *pENT = (INT *)&Buff[0]; int i; clrscr(); highvideo(); randomize(); // --- Llenado del arreglo de char -------------------for(i=0;i<DIM-1;i++) *pENT++=500+random(501); *(pENT)=(INT)(&Buff[0]); pENT = (INT *) (*((INT *)&Buff[22])); cprintf("Direcc de Buff=%u =%u\r\n",Buff,pENT); if(getch()==ESC) return; for(pENT=(INT *)(*((INT *)&Buff[22]));;) { cprintf(" %d ",*pENT++); if(*pENT==(INT)&Buff[0]) { pENT=(INT *)(*((INT *)&Buff[22])); if(getch()==ESC) return; else cprintf("\r\n"); } } } // ----------------------------------------------------------
Siempre es conveniente hacer un esquema para facilitar la visualizacin del problema: 11 elementos unsigned int Buff [ ]
N1 Pg 7/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti
pENT
(INT *)
Este arreglo char de 22 elementos ser llenado con 11 elementos de tipo unsigned int que ocupan 2 bytes cada uno. Entonces lo ms cmodo es utilizar un puntero explcito de ese tipo y recorrer el arreglo partiendo del domicilio [0]: for(i=0;i<DIM-1;i++) *pENT++=500+random(501); *(pENT)=(INT)(&Buff[0]); La segunda instruccin asigna en el domicilio 22 del arreglo, la direccin del domicilio [0]: (INT)(&Buff[0]). El ltimo domicilio contiene la direccin de inicio de la cadena:
randomize( ) random(int N)
La instruccin randomize( ) debe utilizarse una sola vez, normalmente como una de las primeros comandos del main( ), y asegura que cada vez que se corra el programa se generar una secuencia distinta al utilizar random( ). En cuanto a random( ) puede utilizarse de varias maneras tiles:
Imaginemos que deseamos una secuencia de aleatorios entre -10 y 100. Conviene siempre hacer un sencillo esquema que nos gue:
-10
100
N1 Pg 8/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti
Cuando el aleatorio sea igual a 0 (cero) deberemos tener el valor 10, y cuando el aleatorio sea mximo deberemos tener 100. Ello nos lleva a: N = -10 + random(x) 100 = -10 + random(x) random(x) = 100 + 10 = 110 lo cual equivale a decir que x = 111 puesto que genera valores entre 0 y (N-1). Finalmente: N = 10 + random(111). Este razonamiento deber aplicarse para cualquier rango. Ahora cabe la pregunta por el milln podrn generarse valores aleatorios decimales? Respuesta: SI. double RandDec(int Linf, int Lsup, int NDec) { int Divisor=1; int i; double xRand; for(i=1;i<=NDec;i++) Divisor*=10; xRand=(double)Linf+random(Lsup-Linf+1)+ (double)random(Divisor+1)/(double)Divisor; return(xRand); } La generacin aleatorio deber hacerse por partida doble: para la parte entera y para la parte decimal: Parte Entera = (double)(Linf + random(Lsup+Linf+1)) Parte Decimal = (double)random(Divisor + 1) / (double) Divisor y luego sumarse a fin de adicionar la parte decimal a la parte entera.
Arreglos y apuntadores.
Hemos dicho que los arreglos son un caso particular para C, puesto que los maneja como punteros implcitos: el nombre del arreglo contiene la direccin del primer domicilio del mismo. Cuando declaramos:
int Buff[DIM];
Buff contienen la direccin de Buff[0]. Esto hace posible que un arreglo esttico (el que acabamos de declarar) pueda ser manejado de la siguiente manera:
for(i=0;i<DIM;i++) *(Buff+i)=10+random(51);
cuando siempre estuvimos acostumbrados a escribir: Buff[i]=10+random(51) Bueno, es exactamente lo mismo y funcionan correctamente. Con esto podemos inferir que:
N1 Pg 9/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti
La parte izquierda se denomina notacin subindexada y la de la derecha indexada. Esto est ntimamente ligado a la tipificacin. Cuando nosotros escribimos:
Buf++ = Buff + 1*sizoef(tipo del apuntador) Buff + i = Buff + i*sizeof(tipo del apuntador)
En cambio en los punteros no-tipificados (void*) el compilador no tiene forma de saber nuestras intenciones cuando le indicamos ++ Apuntador + i, por lo tanto a estos apuntadores no pueden aplicrseles aritmtica de punteros. Lo que no puede hacerse con el manejo de arreglos como punteros, es alterar la direccin del mismo. Por ejemplo hacer Buff =12500, puesto que intentaramos reasignar la direccin de inicio con el riesgo de crear un gran perjuicio en el programa. Si los elementos que van a almacenarse en el arreglo corresponden con el tipo declarado para el mismo, con la notacin clsica subindexada: Buff[i] tenemos ms que suficiente y no necesitamos complicarnos la vida. Sin embargo si vamos a almacenar otro tipo de dato como por ejemplo enteros, necesitamos forzosamente utilizar un puntero auxiliar o recurrir a la notacin implcita mediante un cast.
DIM*sizeof(int);
Para cargar el arreglo con enteros hacemos:
for(i=0;i<DIM;i+) *pBuff++=10+random(41);
La notacin *pBuff++ posee el siguiente orden de precedencia (o prioridades):
N1 Pg 10/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti
((int *)&Buff[0])
transforma una direccin a char en una direccin a int. La lnea anterior es, por lo tanto, un puntero a int (int *). En ese momento recin podemos incrementar su direccin con plena confianza sabiendo que la misma crecer como i*sizeof(int) y no de otra manera. Este es un cuidado que debemos tener: Convertir la direccin al tipo correcto y recin incrementar su direccin. Lo trgico de esto es que si hacemos mal, o no hacemos la conversin de tipo, todo parece funcionar bien, pero no es as y el error es tan sutil que slo un experto puede detectarlo. La notacin: *( ((int *)(&Buff[0])+i ) ) tambin puede reestructurarse como:
((int *)(&Buff[0])[ i ]
que representa exactamente lo mismo, segn vimos anteriormente. Aqu hemos mezclado un poco de notaciones puramente de apuntadores con la clsica subindexada de arreglos. El siguiente ejemplo engloba todo lo visto hasta aqu. #include<conio.h> #include<stdlib.h> const DIM = 10; // ----------------------------------------------------------void main() { char Buff[DIM*sizeof(int)]; int* pBuff; int i; clrscr(); highvideo(); randomize(); pBuff=(int *)&Buff[0]; // --- PRIMERA FORMA DE RECORRER UN ARREGLO ---------
N1 Pg 11/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti
for(i=0;i<DIM;i++) cprintf("%4d",*(pBuff+i)=10+random(41)); cprintf("\r\n\r\n"); // --- SEGUNDA FORMA DE RECORRER UN ARREGLO --------for(i=0;i<DIM;i++) cprintf("%4d",((int *)&Buff)[i]); cprintf("\r\n\r\n"); // --- TERCERA FORMA DE RECORRER UN ARREGLO --------for(i=0;i<DIM;i++) cprintf("%4d", getch(); } // ------------------------------------------------------------*(((int *)&Buff)+i));
La instruccin: cprintf("%4d",*(pBuff+i)=10+random(41));
realiza dos tareas a la vez: asigna un valor a lo que est referenciando el apuntador y a continuacin lo muestra por pantalla. Esta propiedad de anidar varias instrucciones una dentro de otra, se denomina implicitacin. La asignacin en s es: ",*(pBuff+i)=10+random(41)); Esta notacin para incrementar la posicin de un apuntador se llama indexada puesto que utiliza un ndice, y est relacionada con lo que se denomina aritmtica de punteros, lo cual veremos con ms detenimiento en el apartado siguiente.
Aritmtica de punteros.
Si bien ya utilizamos en forma natural esta aritmtica en los ejemplos anteriores, conviene analizarlo ms detalladamente para completar la idea. La aritmtica de apuntadores consiste en una sintaxis especial que produce cambios en la direccin apuntada. Existen varias posibilidades:
N1 Pg 12/13
Asignatura: TALLER DE LENGUAJES I 2013 Carrera: PROGRAMADOR UNIVERSITARIO / LICENCIATURA EN INFORMATICA Dictado: Ing. Juan Manuel Conti
*(pENT++) (*pENT)++ *(pENT+i) Usa lo referenciado e incrementa direccin. Utiliza lo referenciado e incrementa lo referenciado. Utiliza lo referenciado y luego incrementa en i la direccin.
No conviene sobrecargar demasiado la notacin debido a que va perdiendo claridad y resulta complicado cuando debemos depurar algn error.
pENT[ i ] *(pENT + i)
El contenido intrnseco del apuntador no cambia: contina apuntando a la direccin original, y con respecto a ella consideramos tantos lugares hacia delante (o hacia atrs, si el caso lo requiere).
N1 Pg 13/13