Указатели в C#Автор: Александр Игнатьев
Источник: http://www.daoto.net/, 
http://docs.com.ru/Одним из преимуществ старого (неуправляемого) C++ - был прямой доступ к памяти, обеспечиваемый механизмом указателей. Это позволяло создавать высокпроизводительные приложения и являлось головной болью многих программистов. По себе знаю насколько было проблемно работать с ними. Многие программисты считают, что в C# не поддерживаются указатели. И называют это одним из его достоинств, позволяющим создавать безопасный, легко сопровождаемый код. На самом деле 
в C# есть указатели, просто работа с ними несколько затруднена и ограничена по сравнению с C++.
Код, использующий указатели, должен компилироваться с указанием ключа 
/unsafe:
В Visual Studio .NET этот параметр задается в 
Properties проекта. На закладке 
Build следует установить в 
true параметр 
Allow unsafe code blocks. Кроме того, метод в котором используются указатели должен быть помечен ключевым словом 
unsafe.
Указатели могут объявлятся только на следующие типы:
   • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool.
   • Перичисление.
   • Определенная пользователем структура, содержащая только вышеперечисленные типы.
Указатель объявляется с помощью маркера 
*:
Указатель в C#, так же как и в С++ представляет собой адрес в памяти. Получить адрес переменной можно с помощью маркера 
&:
int  i = 5;
 
int* p = &i;[/c]
 
 
 
В C# также поддерживается арифметика указателей, операторы инкремента и дикремента, оператор [b][][/b]:
 
[code]public unsafe static void Main(string[] args) 
 
{
 
    int i = 5;
 
    int* p1 = &i; 
 
    Console.WriteLine("{0} {1} {2}", i, *p1, p1[0]);
 
    //Out: 5, 5, 5
 
 
 
    //следующие два присваивания равносильны
 
    p1[1] = 6;
 
    *(p1+1) = 6;
 
 
 
    int* p2 = p1+1;
 
    Console.Write("{0} {1}", *p2, p2[0]);
 
    Console.WriteLine("{0} {1}", *(p1+1), p1[1]);
 
    //Out: 6 6 6 6
 
 
 
    p1++;
 
    Console.Write("{0} {1} {2}", *p1, *p2, p1[-1]);
 
    //Out: 6 6 5
 
}
 
Я думаю, понятно почему этот код называется опасным (unsafe). Где гарантия, что по измененному нами адресу 
p1+1 не содержалась важная информация?
Для выделения памяти используется конструкция 
stackalloc T[E] (правда, напоминает malloc в С?), где 
T - один из вышеперечисленных типов. Эта конструкция резервирует память размером 
E*sizeof(E):
int* pArr = stackalloc int[10];
 
Кроме того, указатели могут быть в качестве принимаемых и возвращаемых значаний функции:
public unsafe int* func(int* p, int i)
 
{
 
    *p = i;
 
    return p;
 
}
 
 Конструкция fixedПри обращении к массиву в .NET проводится проверка индексов, что может привести к значительно снижению производительности. К примеру время рассчета одной механической модели методом Либмана:
C++     (VC++ 6.0)    1410 мс
 
C#       (safe)          2910 мс
 
C#       (unsafe)       1560 мс
 
Java                       3100 мс
 
Как видите, отказ от проверки допустимости значений индекс дал выигрыш практически в 2 раза. И C# практически догнал по производительности C++. И это в реализации численного метода! Это говорит о высокой производительности C#.
Быстрый доступ к элементам массива (без проверки допустимости индексов) осуществляется следующим образом:
int[] narr = new int[100];
 
//объявляем указатель на первый элемент массива
 
int* p = &narr[0];
 
//и так можно:
 
//int* p = narr;
 
//теперь мы можем обратиться к любому элементу массива вот так
 
p[10]=p[9]+1;
 
//и без проверки границ!
 
//так что за последствия следующего кода будете отвечать сами:
 
p[200]=100;
 
А теперь представьте, объявили вы указатель на массив, работаете с ним. И тут запускается сборщик мусора, очищает память, деврагметирует ее. Интересно, а где теперь находится ваш массив? А кто его знает... Зато указатель указвает туда же. И все... Приехали. Дальше вы будете долбать данные своей проги, пока не нарветесь на исключение, а то вообще не нарветесь. Просто получите неверный результат, что гораздо хуже.
Вот для таких ситуаций и создана конструкция 
fixed:
fixed(int* p = narr)
 
{
 
    //а тут мы спокойно работаем с нашм указателем
 
    //сборщик мусора шаш массив не тронет...
 
}
 
А как же быть с многомерыми массивами? А только так:
int Nx = 10, Ny = 15;
 
double[,] darr = new dpuble[Nx, Ny];
 
fixed(double* p = &darr[,])
 
{
 
    //обращаемся к элементу darr[5,4]
 
    double d = p[5*Ny + 4];
 
}
 
К сожалению именно так. А с jagged-массивами (double[][]) даже так не проходит...
Как видите, C# не очень хорошо приспособлен для работы с указателями. Да и в большинстве случаев можно обойтись без них, но если уж приспичит... Мне вот они понадобились...