Поиск…


замечания

Указатели и unsafe

Из-за своей природы указатели производят непроверяемый код. Таким образом, использование любого типа указателя требует unsafe контекста.

Тип System.IntPtr - безопасная оболочка вокруг void* . Он предназначен как более удобная альтернатива void* когда небезопасный контекст не требуется для выполнения задачи.

Неопределенное поведение

Как и в C и C ++, неправильное использование указателей может вызывать неопределенное поведение, при этом возможны побочные эффекты, являющиеся повреждением памяти и выполнением непреднамеренного кода. Из-за непроверяемого характера большинства операций указателя правильное использование указателей полностью зависит от программиста.

Типы, которые поддерживают указатели

В отличие от C и C ++, не все типы C # имеют соответствующие типы указателей. Тип T может иметь соответствующий тип указателя, если применимы оба следующих критерия:

  • T - тип структуры или тип указателя.
  • T содержит только элементы, которые рекурсивно удовлетворяют обоим этим критериям.

Указатели для доступа к массиву

В этом примере показано, как указатели могут использоваться для доступа C к массивам C #.

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

unsafe ключевое слово требуется, поскольку доступ к указателю не будет выдавать никаких проверок границ, которые обычно испускаются при регулярном доступе к массивам C #.

fixed ключевое слово сообщает компилятору C #, чтобы он выдавал инструкции для привязки объекта безопасным способом. Для обеспечения того, чтобы сборщик мусора не перемещал массив в памяти, требуется принудительное закрепление, поскольку это приведет к недействительности указателей, указывающих внутри массива.

Арифметика указателя

Сложение и вычитание в указателях работают иначе, чем целые. Когда указатель увеличивается или уменьшается, адрес, на который он указывает, увеличивается или уменьшается по размеру типа референта.

Например, тип int (псевдоним для System.Int32 ) имеет размер 4. Если int может быть сохранен в адресе 0, последующий int может быть сохранен в адресе 4 и так далее. В коде:

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

Аналогично, тип long (псевдоним для System.Int64 ) имеет размер 8. Если long может быть сохранен в адресе 0, последующий long может быть сохранен в адресе 8 и так далее. В коде:

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

Тип void является специальным, а указатели void также являются специальными, и они используются в качестве уловки, когда тип неизвестен или не имеет значения. Из-за их размерно-агностического характера указатели void не могут увеличиваться или уменьшаться:

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));

Звездочка является частью типа

В C и C ++ звездочка в объявлении переменной указателя является частью объявляемого выражения . В C # звездочка в объявлении является частью типа .

В C, C ++ и C # следующий фрагмент объявляет указатель int :

int* a;

В C и C ++ следующий фрагмент объявляет int указатель и переменную int . В C # он объявляет два указателя int :

int* a, b; 

В C и C ++ следующий фрагмент объявляет два указателя int . В C # это недопустимо:

int *a, *b;

недействительным *

C # наследует от C и C ++ использование void* как агностик типа-агностик и размер-агностик.

void* ptr;

Любой тип указателя можно присвоить void* используя неявное преобразование:

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

Обратное требует явного преобразования:

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

Доступ пользователей с помощью ->

C # наследует от C и C ++ использование символа -> как средство доступа к членам экземпляра с помощью типизированного указателя.

Рассмотрим следующую структуру:

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

Это пример использования -> для доступа к его членам:

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

Общие указатели

Критерии, которые должен удовлетворять тип для поддержки указателей (см. Примечания ), не могут быть выражены в терминах общих ограничений. Поэтому любая попытка объявить указатель на тип, предоставленный с помощью параметра типового типа, не будет выполнена.

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
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow