Vous êtes sur la page 1sur 12

**** Punteros

en Delphi ****

Lisa && Alquimista


Delphi a primera vista casi no usa los punteros, pero entre bastidores los punteros son muy usados, y existe una escasa informacin, sobre que son y como usarlos, as pues primero vamos a ver que es un puntero. En una computadora cada posicin de memoria tiene una direccin y un valor especfico almacenado en esa posicin. En la programacin tradicional, se han utilizado nombres de variables en lugar de direcciones porque los nombres son ms fciles de recordar. Para almacenar un nuevo valor en memoria se asigna a una variable, y la computadora enva una direccin a la memoria seguida por el valor a almacenar en esa posicin. Ej. X := 10; Delphi proporciona un tipo especial de variable denominado puntero. Puntero -> Una variable cuyo valor es una direccin de una posicin de memoria. Formato: Type tipo_puntero = ^tipo dato Una variable de tipo tipo_puntero es un puntero hacia un dato de tipo nombre del tipo. El signo '^' se lee "apunta hacia".

Al definir un tipo puntero se debe indicar el tipo de valores que se almacenarn en las posiciones designadas por los punteros. La razn es que los diferentes tipos de datos requieren diferentes cantidades de memoria para almacenar sus constantes, una variable puntero puede contener una direccin de una posicin de memoria adecuada slo para un tipo dado. Por esta razn se dice que un puntero apunta a una variable particular. Como comentbamos anteriormente, una variable tipo puntero contiene la direccin de memoria de la posicin de otra variable Para declarar una variable de tipo puntero en Delphi se debe especificar el nombre de la variable y el tipo del valor que se almacenar en la posicin de memoria a la que el puntero se refiere. Una vez que haya definido una variable puntero, puede asignarla a la direccin de otra variable del mismo tipo, usando el operador @ : var P: ^Integer; X: Integer; begin P := @X; { Obtiene la posicin de memoria donde esta guardada la Variable X. Una vez que el puntero contiene la direccin de memoria Podemos cambiar el valor de la variable de dos formas. } X := 10; P^ := 20; // Pone 10 en la variable X // Pone 20 en la variable X

Cuando tenga un puntero P, con la expresin P se referir a la direccin de la posicin de memoria a que apunta el puntero, y con la expresin P^ al contenido real de aquella direccin. Por esta razn, en el fragmento de cdigo de arriba, ^P corresponde a X. Bueno llegados a este punto tenemos un montn de teora y poco claros los conceptos, lo mejor ser realizar un pequeo programa,para ver que pasa entre bastidores y as, ver la relacin entre lo que apunta el puntero y la variable a la cual apunta.

Nada mejor que este pequeo ejemplo para asentar lo explicado procedure TForm1.Button1Click(Sender: TObject); Var X :integer; P :^Integer; begin X:=10; P:=@X; Edit1.Text:=InttoHex(Integer(P),8); Edit2.Text:=InttoStr(P^); Edit3.Text:=InttoHex(Integer(@X),8); Edit4.Text:=InttoStr(X); end;

En la captura se ve claramente la relacin entre la variante y el puntero si ponemos un punto de ruptura en: ** Edit1.Text:=InttoHex(Integer(P),8); y observamos en la memoria vemos: -> Posicin de memoria Que el registro EAX tiene la posicin de memoria, y en el cuadro inferior vemos que en dicha posicin de memoria esta el valor de 10 (0A Hex), que es el valor al cual apunta el puntero.

-> Valor de EAX En este otro grafico tambin se puede apreciar la relacin entre lo apuntado y lo que se apunta.

Constante nil
Si un puntero no tiene valor, puede asignarle el valor nil (vaco). Entonces, puede comprobar si un puntero es nil para ver si actualmente apunta a algn valor, puede ser asignada a un puntero de cualquier tipo. (en C esto mismo seria NULL) P:=nil; Como nil no apunta a ninguna posicin de memoria, una referencia a P^ es incorrecta si el valor de P es nil. Esto se hace a menudo, porque desreferenciar un puntero invlido causa una infraccin de acceso (tambin llamada fallo de proteccin general, GPF) : P:= nil; P^:= 22; { Asignacin incorrecta}

Par entender que es tambin llamada fallo de proteccin general, GPF lo mejor ser ejecutar este pequeo programa.
procedure TForm1.Button1Click(Sender: TObject); Var P :^integer; begin P:=nil; ShowMessage(IntToStr(P^)); end;

Como comprobaremos nos muestra una Error en ShowMessage(IntToStr(P^)); ! Se esta intentando visualizar el contenido de un puntero que no apunta a ningn sitio !.

Operaciones con apuntadores


Los Punteros se crean como hemos visto anteriormente y las operaciones que se pueden realizar entre ellos son:
Type PunteroEntero : ^integer; Var P:PunteroEntero;

P es una variable tipo Puntero-Entero que apunta a posiciones que contienen enteros. La posicin de memoria designada por el valor de la variable Puntero P se representa por P^. La siguiente figura representa la relacin entre P y P^. P^:=1000 // el valor de P^ es 1000 3 * P^ + 500 = 3500 Como P^ designa una posicin de memoria, se puede utilizar como cualquier otra variable. Se pueden asignar valores a P^ y utilizar valores P^ en expresiones como cualquier otra variable. Si P apunta a posiciones que contienen reales, P^ es una variable real.

Punteros y Bloques de memoria


En vez de referirse a una posicin de memoria existente, un puntero se puede referir a un nuevo bloque de memoria asignado dinmicamente (en el rea de memoria heap) con el procedimiento GetMem. En este caso, cuando deje de necesitar el puntero, deber deshacerse de la memoria que adjudic dinmicamente, haciendo una llamada al procedimiento FreeMem. Este tema ser explicado ms ampliamente cuando se trate la memoria.

Comparacin de punteros
Los Puntero slo pueden ser comparados en expresiones de igualdad. En el caso de los objetos direccionados por los punteros no existe ninguna restriccin en cuanto al uso de los operadores relacinales. Ejemplo: Asignar el siguiente cdigo a un botn y se pueden comprobar los resultados de la tabla abajo indicada.
procedure TForm1.Button1Click(Sender: TObject); Var X,Y :integer; P,Q :^integer; begin P:=@X; Q:=@Y; P^:=10; Q^:=20; /// Comprobar igualadades if if if if end; P= Q P<>Q P^ > Q^ P^ <= Q^ then then then then ShowMessage('P = Q'); ShowMessage('P <> Q'); ShowMessage('P^ > Q^'); ShowMessage('P^ <= Q^');

Tabla de relacin entre comparacin de punteros Sentencia then then then Resultado false true Sentencia no vlida. En punteros no se pueden usar los siguientes operadores relacionales >,>=,<,<=. false true

if P = Q if P <> Q if P > Q

if P^ > Q^ then if P^ <= Q^ then

Tipo de puntero genrico (pointer)


Delphi permite un tipo especial de definicin de Puntero: "genrico" o "no tipeado". Difiere del puntero estndar en que no tiene un tipo base, no est definido como un puntero hacia algn tipo, sino simplemente como una variable de tipo pointer. Un puntero sin tipo es : (como el void* en el lenguaje C). Este tipo de punteros es muy usado cuando una funcin o procedimiento requiere un puntero en uno de sus parmetros como por ejemplo . WriteProcessMemory ( veamos que pone la documentacin.. ) The WriteProcessMemory function writes memory in a specified process. The entire area to be written to must be accessible, or the operation fails. No se traduce esta funcin ser ampliamente comentada en otros ensayos, ahora solo se pretende mostrar el parmetro marcado, que es un puntero. BOOL WriteProcessMemory ( HANDLE hProcess, //handle to process whose memory is written to LPVOID lpBaseAddress, // address to start writing to LPVOID lpBuffer, // pointer to buffer to write data to DWORD nSize, // number of bytes to write LPDWORD lpNumberOfBytesWritten // number of bytes written ); Vemos claramente, que el parmetro marcado es un puntero, as pues este parmetro, ser un puntero sin tipo o genrico. Esto podra ser una llamada a dicha funcin. WriteProcessMemory (BwProcessInfo.hProcess, Pointer (PatchOffset), @PatchValue[i], sizeof (PatchValue[i]), BuffCount);

Bueno pues como teora eso es todo, durante el proyecto de realizaran muchas practicas con punteros, y los conocimientos adquiridos en esta introduccin quedaran muy asentados, hay otro tema que al ser especifico de Delphi merece la pena tratar antes de introducirse de lleno en el ensayo, pues un conocimiento ligero de este puede dificultar mucho seguir el curso dicho tema son las Cadenas en formato C.

Cadenas en formato C
Cadenas en formato C ? Cadenas largas de Delphi ? Que son y que diferencia hay entre ambas, la razn de tratar este tema es que en el proyecto inevitablemente nos introduciremos de lleno en el mundo de las API y estas requieren las Cadenas en formato C. Nada emjor que una representacin grafica para verlo, representadas mediante el tipo PChar, para poder realizar llamadas a funciones del API que necesitan cadenas. Una cadena en este formato se representa como un puntero a un vector de caracteres, pero, a diferencia de lo que sucede en Pascal, el primer carcter del vector con tiene directamente el primer carcter de la cadena. Para indicar el final de sta, se coloca un carcter nulo, cuyo cdigo es 0, inmediatamente despus del ltimo carcter. El siguiente grfico muestra la forma de representacin de cadenas al estilo C.

Es importante darse cuenta de que una variable declarada de tipo Pchar solamente contiene 4 bytes: el espacio necesario para el puntero. La zona de memoria a la cual apunta esta va riable puede suministrarse de varias formas. Puede ser, por ejemplo, memoria rese rvada en la memoria dinmica (heap), pero tambin puede ser un vector declarado en memoria global o de pila.

Cadenas de caracteres largas en Delphi


Con Delphi 2 se hizo necesario ampliar la capacidad de las cadenas de caracteres. Para ello se introdujo el nuevo tipo de datos AnsiString , que permite almacenar hasta 2 elevado a 31 1 caracteres. Tambin se ha cambiado el significado del tipo predefinido string A partir de la versin 2, el tipo string es un sinnimo del tipo AnsiString . La implementacin de las cadenas AnsiString es una mezcla de la implementacin de las cadenas de C y de las antiguas cadenas cortas de Pascal. Una variable AnsiString ocupa exactamente 4 bytes, pues con tiene un puntero. Si la cadena est vaca, el puntero es nulo; en caso contrario, apunta a una cadena de caracteres en estilo C, esto es, terminada en un carcter nulo. El espacio de memoria en el que se representala cadena reside en la memoria dinmica de Pascal. Hasta aqu el parecido con C. Pero el bloque de memoria donde se guardan los datos contiene tambin la longitud de la cadena y un contador de referencias, sobre el que trataremos ms adelante. Dnde se guarda esta informacin? Al final de la cadena? No, se guarda antes! El siguiente grfico ayuda a comprender la situacin.

Como vemos, el puntero almacenado en la variable de cadena no apunta al principio del bloque de memoria, sino 8 bytes ms adelante. El objetivo de este truco es facilitar la conversin de estas cadenas para utilizarlas en llamadas al API de Windows pues, ignorando el prefijo que contiene la longitud y el contador de referencias, el diseo en memoria de este tipo de datos lo hace indistinguible de las cadenas C. Para evitar que el programador tenga que ocuparse directamente de pedir y liberar memoria para el buffer de datos, Delphi implementa un ingenioso mecanismo basado en contadores de referencia: cada cadena recuerda la cantidad de variables que apuntan a la misma. Cada vez

que se asigna un cadena a una variable, se incrementa el contador de la cadena asignada. Si la variable apuntaba antes a otra cadena, la cadena que se pierde decre menta su contador; cuando el contador llega a cero, se libera automticamente la memoria reservada. El decremento del contador de referencias tambin ocurre para las variables locales, al terminar su tiempo de vida: ( informacin obtenida del libro la cara oculta de delphi 4 ).

Si desea incrementar el tamao de una cadena en la memoria pero hay otra cosa en la memoria adyacente, la cadena no puede crecer en esa posicin, y por tanto debe hacerse una copia completa de la cadena en otro emplazamiento. Cuando ocurre esta situacin, los algoritmos de ejecucin de Delphi reasignan la variable por nosotros, de forma totalmente transparente. Simplemente establezca el tamao de la cadena mediante el procedimiento SetLength, adjudicando de forma efectiva la cantidad requerida de memoria: SetLength (String1, 200); En realidad, el procedimiento SetLength realiza una peticin de memoria, no una asignacin directa de memoria. Reserva la cantidad de memoria requerida para usarla despus, sin usar la memoria de hecho. Esta tcnica se basa en una caracterstica de los sistemas operativos Windows y es usado por Delphi en todas las asignaciones dinmicas de memoria. Por ejemplo, cuando establece un vector muy grande, su memoria se reserva, pero no se adjudica. Fijar la longitud de una cadena es raramente necesario. El nico caso en que se debe asignar memoria a cadenas largas mediante SetLength es cuando hay que utilizarlas como parmetros para una funcin API (usando el typecast o modelado correspondiente). Nosotros trasformaremos las cadenas largas de Delphi en Pchar con un simple typecast y casi siempre para pasarlas como prametros a las API de Windows.

Con un ejemplo nos quedara ms claro: Por ejemplo, para copiar el ttulo de un formulario a una cadena PChar (usando la funcin API GetWindowText) y luego copiarla a la leyenda de un botn, se puede escribir el siguiente cdigo : procedure TForm1.Button1Click (Sender: TObject); var S1: String; begin SetLength (S1, 100); GetWindowText (Handle, PChar (S1), Length (S1)); Button1.Caption := S1; end; En Azul negrilla se puede ver el typecast. Una cosa muy importante es : Cuando convertimos mediante un Typecast una cadena Delphi a un PChar luego seremos nosotros los responsables de esa cadena y no podremos utilizar funciones de cadenas sobre dicha cadena como por ejemplo el operador +. Este ensayo solo pretende aclarar las cosas de cmo las cadenas son tratadas en Windows y como son tratadas en Delphi, para nosotros como usuarios, normalmente solo tendremos que realizar un Typecast, cuando llamemos a una API, por lo dems todo ser transparente para nosotros. Bueno aqu acaba el tutorial de introduccin, si han sido capaces de pasar esta introduccin, con 0 de practicas, felicidades ahora habr mucha prctica, para asentar los conocimientos.

Vous aimerez peut-être aussi