C# Language
указатели
Поиск…
замечания
Указатели и 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
}