04 diciembre 2006

Punteros parte #2

Operador stackalloc. Creación de tablas en pila.

Cuando se trabaja con punteros puede resultar interesante reservar una zona de memoria en la pila donde posteriormente se puedan ir
almacenando objetos. Precisamente para eso está el operador stackalloc, que se usa siguiéndose la siguiente sintaxis:

stackalloc []

stackalloc reserva en pila el espacio necesario para almacenar contiguamente el número de objetos de tipo indicado en (reserva sizeof()* bytes) y devuelve un puntero a la dirección de inicio de ese espacio. Si no quedase memoria libre suficiente para reservarlo se produciría una excepción System.StackOverflowException.

stackalloc sólo puede usarse para inicializar punteros declarados como variables locales y sólo en el momento de su declaración.. Por ejemplo, un puntero pt que apuntase al principio de una región con capacidad para 100 objetos de tipo int se declararía con:

int * pt = stackalloc int[100];

Sin embargo, no sería válido hacer:

int * pt;
pt = stackalloc int[100]; // ERROR: Sólo puede usarse stackalloc en declaraciones

Aunque pueda parecer que stackalloc se usa como sustituto de new para crear tablas en pila en lugar de en memoria dinámica, no hay que confundirse: stackalloc sólo reserva un espacio contiguo en pila para objetos de un cierto tipo, pero ello no significa que se cree una tabla en pila. Las tablas son objetos que heredan de System.Array y cuentan con los miembros heredados de esta clase y de object, pero regiones de memoria en pila reservadas por stackalloc no. Por ejemplo, el siguiente código es inválido.

int[] tabla; int * pt = stackalloc int[100];
tabla = *pt; // ERROR: El contenido de pt es un int, no una tabla (int[])
Console.WriteLine(pt->Length); // ERROR: pt no apunta a una tabla
Sin embargo, gracias a que como ya se ha comentado en este tema el operador [] está redefinido para trabajar con punteros, podemos usarlo para acceder a los diferentes objetos almacenados en las regiones reservadas con stackalloc como si fuesen tablas. Por ejemplo, este código guarda en pila los 100 primeros enteros y luego los imprime:

class Stackalloc
{
public unsafe static void Main()
{
int * pt = stackalloc int[100];
for (int i=0; i<100; i++)
pt[i] = i;
for(int i=0; i<100; i++)
System.Console.WriteLine(pt[i]);
}
}


Fijación de variables apuntadas

Aunque un puntero sólo puede apuntar a datos de tipos que puedan almacenarse completamente en pila (o sea, que no sean ni objetos de tipos referencia ni estructuras con miembros de tipos referencia), nada garantiza que los objetos apuntado en cada momento estén almacenados en pila. Por ejemplo, las variables estáticas de tipo int o los elementos de una tabla de tipo int se almacenan en memoria dinámica aún cuando son objetos a los que se les puede apuntar con punteros.

Un uso frecuente de fixed consiste en apuntar a objetos de tipos para los que se puedan declarar punteros pero que estén almacenados en tablas, ya que ello no se puede hacer directamente debido a que las tablas se almacenan en memoria dinámica. Por ejemplo, copiar usando punteros una tabla de 100 elementos de tipo int en otra se haría así:

class CadenaInsegura {
public unsafe static void Main()
{
string s=”Hola”;
Console.WriteLine(“Cadena inicial: {0}”, s);
fixed (char * ps=s)
{
for (int i=0;i }
Console.WriteLine(“Cadena final: {0}”, s);
}
}
La salida por pantalla de este último programa es:
Hola
AAAA

Cuando se modifiquen cadenas mediante punteros hay que tener en cuenta que, aunque para facilitar la comunicación con código no gestionado escrito en C o C++ las cadenas en C# también acaban en el carácter ‘\0’, no se recomienda confiar en ello al recorrerlas con punteros porque ‘\0’ también puede usarse como carácter de la cadena. Por ello, es mejor hacer como en el ejemplo y detectar su final a través de su propiedad Length.

No hay comentarios.: