Los punteros de C en C#

portada

Los punteros son una de las más potentes características de C, pero a la vez uno de sus mayores peligros. Ahora los punteros de C en C# no se suelen usar, puesto que tenemos varias estructuras dinámicas ya creadas como parte de las bibliotecas auxiliares que acompañan al lenguaje básico. Los punteros nos permiten acceder directamente a cualquier parte de la memoria creando estructuras dinámicas. Esto da a los programas C una gran potencia y a la vez una cantidad ilimitada de problemas, y por eso es inseguro usarlos.

Pero les voy a explicar de todas formas como usar los apuntadores en el lenguaje de la tecnología .NET como si estuviéramos aprendiendo C.

Para empezar vamos a diferenciar como se declaran las variables punteros y las variables normales a las que estamos acostumbrados


int numero; // variable normal
int* posicion; // variable puntero a entero (dirección de memoria en la que podremos guardar un entero) 
int *posicion2; // también se pueden declarar de esta manera

Las variables normales y las variables punteros por más que sean enteros, no son del mismo tipo. La variable int contiene un valor numérico, en cambio un puntero de int contiene la referencia a una posición en memoria reservada para un valor numérico.

Ahora, ¿Como puedo asignar un valor a esa posición en memoria?


int edad = 21;
int* pos; 
pos = &edad; 

Con el símbolo ‘&’ hacemos que ‘pos‘ apunte a ‘edad. De esta manera, ahora la posición que almacene el puntero ‘pos‘ será la que el compilador le había reservado a la variable ‘edad‘.

Ya tenemos un puntero hacia una posición de memoria, vamos a sacarle provecho….

Pero antes hay que dejar claro que esto:


 pos = pos + 3;

No es lo mismo que hacer esto:


 *pos = *pos + 3;

En el primer caso en C# estamos cambiando la posición de memoria del puntero, o sea, que si estaba en la posición 0004, ahora va a estar en la posición 0007 .

En el segundo caso estamos cambiando el valor de la variable a la que el puntero hace referencia. La variable ‘edad’ la declaramos en 21, ahora el valor será 24. Entonces dejamos claro que al usar ‘*’ en el puntero hacemos referencia a la variable a la que apuntamos.

Por los peligros que tiene usar punteros en un proyecto .NET, debemos permitir usar bloques de códigos no seguros. ¿Que serian estos bloques de códigos no seguros?


public unsafe void UsarPunteros()
{
    int edad = 21;
    int* pos;
    pos = &edad;

    *pos = *pos + 3;
}

A aquellos métodos que usan punteros hay que agregarle la palabra clave ‘unsafe’ .


public static void Main() 
{ 
    unsafe
    {
        int edad = 21;
        int* pos;
        pos = &edad;

        *pos = *pos + 3;
    }
  }

También se puede abrir un contexto nuevo unsafe .

¿Alguna vez has usado las palabras clave ref o out?

Quiero que te tomes una pausa y veas el siguiente código


public unsafe void Incrementar(int* numero)
{
    *numero = *numero + 1;
}

Tenemos el método Incrementar que recibe como parámetro un puntero y al valor al que apunte le incrementa 1.


int cantidad = 0;
unsafe
{
    Incrementar(&cantidad);
}
// cantidad es igual a 1

Entonces para poder usar este método solo debo indicarle al parámetro la posición en memoria de cierta variable. Esto es lo mismo que utilizar un parámetro normal ref o out porque declaramos una variable, reservamos un espacio de memoria y le damos al método la posición de ese espacio para que pueda alterar el valor.

Un array es un conjunto contiguo de espacios de memoria. Para poder crear un array con los punteros de C en C#, vamos a usar la palabra clave ‘stackalloc’ que nos permite tener un puntero que apunte a esos espacios de memoria contiguos.

En el siguiente código vemos como creamos un puntero que apunte hacia un array y a cada espacio de memoria le asignamos un valor.


unsafe
{
    const int tamañoArray = 5;
    int* datos = stackalloc int[tamañoArray];

    for (int i = 0; i < tamañoArray; i++)
    {
        datos[i] = i * 10;
    }
    //0
    //10
    //20
    //30
    //40

}

Cuando el entorno de ejecución del programa identifica que hay espacios en memoria que ya no se usan, se liberan. Acá es donde empiezan los problemas ya que puede ser el caso de que sigamos usando ese espacio.

Por eso es que existe un contexto de C# llamado 'fixed' que es donde se fija un puntero a un espacio de memoria y queda en solo lectura, de manera de que no se pueda modificar ni eliminar esa memoria.


public static void Main() 
{ 
    unsafe
    {
        int[] datos = { 10, 20, 30 };

        Console.WriteLine("Leyendo el segundo dato...");
        fixed (int* posicionDato = &datos[1])
        {
            Console.WriteLine("En posicionDato hay {0}", *posicionDato);
        }

        Console.WriteLine("Leyendo el primer dato...");
        fixed (int* posicionDato = datos)
        {
            Console.WriteLine("Ahora en posicionDato hay {0}", *posicionDato);
        }
    }
  }

Con esto finalizamos este articulo donde vimos como hacer uso de los espacios de memoria con los punteros de C en C#. Cuando necesitemos hacer aplicaciones estrictas con la memoria esta información nos sera muy útil.

Les dejo el en mi perfil de GitHub un repositorio donde pongo en practica todo lo visto hoy en una aplicación de escritorio para que puedan visualizar los espacios de memorias y sus respectivos valores.
https://github.com/lauchacarro/Punteros-C-Prueba-De-Concepto-