Шаг 211.
Язык программирования C#. Начала.
Указатели. Инструкция fixed

    На этом шаге мы рассмотрим назначение и использование этой инструкции.

    Представим себе такую ситуацию. В программе создан объект класса, и у этого объекта есть поле (например, целочисленное). Мы не можем создать указатель для объекта, но можем создать указатель для поля, поскольку фактически это некоторая переменная. Но при этом мы сталкиваемся с потенциальной проблемой. Дело в том, что если в какой-то момент окажется, что в программе ссылок на объект больше нет (а это вполне реальная ситуация), то объект из памяти может быть удален. И тогда указатель будет ссылаться на область памяти, которая больше не используется для хранения значения поля. Такие ситуации считаются (и являются) потенциально опасными. Они блокируются: просто так указатель на поле объекта не создать, приходится использовать "специальный подход".


То, что на поле объекта ссылается указатель, само по себе не защищает объект от удаления. В расчет принимаются только объектные переменные, которые ссылаются на объект. Если таких ссылок нет, объект помещается в очередь на удаление.

    Для предотвращения удаления (системой сборки мусора) переменных (например, поля объекта), на которые ссылаются указатели, используется инструкция fixed. Шаблон использования этой инструкции представлен ниже:

fixed(тип* указатель = &переменная){
  // Команды
}

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

using System;

using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace pr211_1
{
    // Класс с полем: 
    class MyClass {
        // Поле:
        public int number;
        // Конструктор: 
        public MyClass(int n) { 
            number = n;
        }
        // Деструктор:
        ~MyClass() {
            Console.WriteLine("Удален объект с полем " + number);
        }
    }

    // Главный класс программы:
    class Program
    {
        // Главный метод:
        unsafe static void Main()
        {
            // Использован fixed-блок: 
            fixed (int* p = &new MyClass(123).number)
            {
                // Отображение значения поля:
                Console.WriteLine("Значение поля number: " + *p);
                // Полю присваивается новое значение:
                *p = 321;
                // Отображение сообщения:
                Console.WriteLine("Завершение fixed-блока");
            }
        }
    }
}
Архив проекта можно взять здесь.

    Результат выполнения программы такой:


Рис.1. Результат выполнения программы

    В программе описывается класс MyClass, у которого есть открытое целочисленное поле number, конструктор с одним аргументом (определяет значение поля), а также деструктор, который при удалении объекта из памяти выводит в консоль соответствующее сообщение со значением поля number удаляемого объекта.

    Главный метод состоит из fixed-блока, в котором объявлен указатель p на целочисленное значение, и этому указателю в качестве значения присвоено выражение &new MyClass(123).number. Проанализируем его. Инструкция new MyClass(123) создает объект класса MyClass, и поле number этого объекта равно 123. Значением инструкции является ссылка на созданный объект. Эта ссылка никуда не записывается, и поэтому объект является анонимным. Сразу после создания он попадает в очередь на удаление. Точнее, должен был бы попасть. Инструкцией new MyClass(123).number мы обращаемся к полю number этого объекта, а символ & перед всем выражением означает, что нас интересует адрес этого поля. Таким образом, значением выражения &new MyClass(123).number является адрес поля number созданного анонимного объекта, и этот адрес записывается в указатель p. В итоге объект, хотя он и анонимный, гарантированно не удаляется из памяти, пока выполняется fixed-блок.

    Значение поля объекта проверяем с использованием выражения *p. Командой

  *p = 321; 
полю объекта присваивается новое значение. После этого завершается выполнение fixed-блока (об этом выводится сообщение) и завершается выполнение главного метода. Но перед этим созданный объект удаляется из памяти. Для данного объекта вызывается деструктор, и в консольном окне появляется сообщение со значением поля удаляемого объекта. Убеждаемся, что речь идет о том объекте, на поле number которого ссылался указатель p.

    На следующем шаге мы рассмотрим связь указателей и массивов.




Предыдущий шаг Содержание Следующий шаг