Está en la página 1de 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; // Pone 10 en la variable X


P^ := 20; // 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 P= Q then ShowMessage('P = Q');


if P<>Q then ShowMessage('P <> Q');
if P^ > Q^ then ShowMessage('P^ > Q^');
if P^ <= Q^ then ShowMessage('P^ <= Q^');

end;

Tabla de relacin entre comparacin de punteros

Sentencia Resultado
if P = Q then false
if P <> Q then true
if P > Q then Sentencia no vlida. En punteros
no se pueden usar los siguientes
operadores relacionales >,>=,<,<=.
if P^ > Q^ then false
if P^ <= Q^ then true
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.

También podría gustarte