Buscar..


Observaciones

Punteros e unsafe

Debido a su naturaleza, los punteros producen código no verificable. Por lo tanto, el uso de cualquier tipo de puntero requiere un contexto unsafe .

El tipo System.IntPtr es una envoltura segura alrededor de un void* . Está pensado como una alternativa más conveniente para void* cuando no se requiere un contexto inseguro para realizar la tarea en cuestión.

Comportamiento indefinido

Al igual que en C y C ++, el uso incorrecto de los punteros puede invocar un comportamiento indefinido, con posibles efectos secundarios como corrupción de la memoria y ejecución de código no deseado. Debido a la naturaleza no verificable de la mayoría de las operaciones de puntero, el uso correcto de los punteros es responsabilidad exclusiva del programador.

Tipos que soportan punteros

A diferencia de C y C ++, no todos los tipos de C # tienen los tipos de punteros correspondientes. Un tipo T puede tener un tipo de puntero correspondiente si se aplican los dos criterios siguientes:

  • T es un tipo de estructura o un tipo de puntero.
  • T contiene solo miembros que satisfacen estos dos criterios recursivamente.

Punteros para acceso a la matriz

Este ejemplo demuestra cómo se pueden usar los punteros para el acceso tipo C a las matrices C #.

unsafe
{
    var buffer = new int[1024];
    fixed (int* p = &buffer[0])
    {
        for (var i = 0; i < buffer.Length; i++)
        {
            *(p + i) = i;
        }
    }
}

La palabra clave unsafe es necesaria porque el acceso del puntero no emitirá ninguna verificación de límites que normalmente se emite al acceder a las matrices de C # de forma regular.

La palabra clave fixed le dice al compilador de C # que emita instrucciones para fijar el objeto de una manera segura y excepcional. La fijación es necesaria para garantizar que el recolector de basura no moverá la matriz en la memoria, ya que esto invalidaría cualquier puntero que apunte dentro de la matriz.

Aritmética de punteros

La suma y la resta en los punteros funciona de manera diferente a los enteros. Cuando un puntero aumenta o disminuye, la dirección a la que apunta aumenta o disminuye según el tamaño del tipo de referencia.

Por ejemplo, el tipo int (alias para System.Int32 ) tiene un tamaño de 4. Si se puede almacenar un int en la dirección 0, el int posterior se puede almacenar en la dirección 4, y así sucesivamente. En codigo:

var ptr = (int*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 4
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8

De manera similar, el tipo long (alias para System.Int64 ) tiene un tamaño de 8. Si se puede almacenar un long en la dirección 0, el long posterior puede almacenarse en la dirección 8, y así sucesivamente. En codigo:

var ptr = (long*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 16

El tipo void es especial y los punteros void también son especiales y se utilizan como punteros de captura cuando el tipo no se conoce o no importa. Debido a su naturaleza de tamaño agnóstico, los punteros de void no se pueden incrementar o disminuir:

var ptr = (void*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));

El asterisco es parte del tipo.

En C y C ++, el asterisco en la declaración de una variable de puntero es parte de la expresión que se declara. En C #, el asterisco en la declaración es parte del tipo .

En C, C ++ y C #, el siguiente fragmento de código declara un puntero int :

int* a;

En C y C ++, el siguiente fragmento de código declara un puntero int y una variable int . En C #, declara dos punteros int :

int* a, b; 

En C y C ++, el siguiente fragmento de código declara dos punteros int . En C #, no es válido:

int *a, *b;

vacío*

C # hereda de C y C ++ el uso de void* como un puntero de tipo agnóstico y de tamaño.

void* ptr;

Cualquier tipo de puntero se puede asignar a void* mediante una conversión implícita:

int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;

Lo contrario requiere una conversión explícita:

int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
int* p2 = (int*)ptr;

Acceso de miembros usando ->

C # hereda de C y C ++ el uso del símbolo -> como un medio para acceder a los miembros de una instancia a través de un puntero escrito.

Considere la siguiente estructura:

struct Vector2
{
    public int X;
    public int Y;
}

Este es un ejemplo del uso de -> para acceder a sus miembros:

Vector2 v;
v.X = 5;
v.Y = 10;

Vector2* ptr = &v;
int x = ptr->X;
int y = ptr->Y;
string s = ptr->ToString();

Console.WriteLine(x); // prints 5
Console.WriteLine(y); // prints 10
Console.WriteLine(s); // prints Vector2

Punteros genéricos

Los criterios que debe cumplir un tipo para admitir punteros (ver Comentarios ) no pueden expresarse en términos de restricciones genéricas. Por lo tanto, cualquier intento de declarar un puntero a un tipo proporcionado a través de un parámetro de tipo genérico fallará.

void P<T>(T obj) 
    where T : struct
{
    T* ptr = &obj; // compile-time error
}


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow