Полное руководство по языку программирования С# 14 и платформе .NET 10 / Сборка мусора, управление памятью и указатели

Указатели на структуры, члены классов и массивы

Начальный ⏱ 5 мин чтения 👁 2 🕓 обновлено 11.08.2026

Указатели на типы и операция ->

Кроме указателей на простые типы можно использовать указатели на структуры. А для доступа к полям структуры, на которую указывает указатель, используется операция ->:


unsafe
{
    Point point = new Point(0, 0);
    Console.WriteLine(point);   // X: 0  Y: 0
    Point* p = &point;

    p->X = 30;
    Console.WriteLine(p->X);    // 30

    // разыменовывание указателя
    (*p).Y = 180;
    Console.WriteLine((*p).Y);  // 180

    Console.WriteLine(point);   // X: 30  Y: 180
}
struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
    public Point(int x, int y)
    {
        X = x; Y = y;
    }
    public override string ToString() => $"X: {X}  Y: {Y}";
}

Обращаясь к указателю p->X = 30; мы можем получить или установить значение свойства структуры, на которую указывает указатель. Обратите внимание, что просто написать p.X=30 мы не можем, так как p - это не структура Point, а указатель на структуру.

Альтернативой служит операция разыменования: (*p).X = 30;

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

Указатели на массивы и stackalloc

С помощью ключевого слова stackalloc можно выделить память под массив в стеке. Смысл выделения памяти в стеке в повышении быстродействия кода. Посмотрим на примере вычисления квадратов чисел:


unsafe
{
    const int size = 7;
    int* square = stackalloc int[size]; // выделяем память в стеке под семь объектов int
    int* p = square;
    // вычисляем квадраты чисел от 1 до 7 включая
    for (int i = 1; i <= size; i++, p++)
    {
        // считаем квадрат числа
        *p = i * i;
    }
    for (int i = 0; i < size; i++)
    {
        Console.WriteLine(square[i]);
    }
}

Оператор stackalloc принимает после себя массив, на который будет указывать указатель. int* square = stackalloc int[size];.

Для манипуляций с массивом создаем указатель p: int* p = square;, который указывает на первый элемент массива, в котором всего 7 элементов. То есть с помощью указателя p мы сможем перемещаться по массиву square.

Далее в цикле происходит подсчет квадратов чисел от 1 до 7. В цикле для установки значения (квадрата числа - i * i) по адресу, который хранит указатель, выполняется выражение:

*p = i * i;

Затем происходит инкремент указателя p++, и указатель p смещается вперед на следующий элемент в массиве square.

Чуть более сложный пример - вычисление факториала:


unsafe
{
    const int size = 7;
    int* factorial = stackalloc int[size]; // выделяем память в стеке под семь объектов int
    int* p = factorial;

    *(p++) = 1; // присваиваем первой ячейке значение 1 и
    // увеличиваем указатель на 1
    for (int i = 2; i <= size; i++, p++)
    {
        // считаем факториал числа
        *p = p[-1] * i;
    }
    for (int i = 0; i < size; i++)
    {
        Console.WriteLine(factorial[i]);
    }
}

Также с помощью оператора stackalloc выделяется память для 7 элементов массива. И также для манипуляций с массивом создаем указатель p: int* p = factorial;, который указывает на первый элемент массива, в котором всего 7 элементов

Далее начинаются уже сами операции с указателем и подсчет факториала. Так как факториал 1 равен 1, то присваиваем первому элементу, на который указывает указатель p, единицу с помощью операции разыменования: *(p++)= 1;

Для установки некоторого значения по адресу указателя применяется выражение: *p=1. Но кроме этого тут происходит также инкремент указателя p++. То есть сначала первому элементу массива присваивается единица, потом указатель p смещается и начинает указывать уже на второй элемент. Мы могли бы написать это так:


*p= 1;
p++;

Чтобы получить предыдущий элемент и сместиться назад, можно использовать операцию декремента: Console.WriteLine(*(--p));. Обратите внимание, что операции *(--p) и *(p--) различаются, так как в первом случае сначала идет смещение указателя, а затем его разыменовывание. А во втором случае - наоборот.

Затем вычисляем факториал всех остальных шести чисел: *p = p[-1] *i;. Обращение к указателям как к массивам представляет альтернативу операции разыменовывания для получения значения. В данном случае мы получаем значение предыдущего элемента.

И в заключении, используя указатель factorial, выводим факториалы всех семи чисел.

Оператор fixed и закрепление указателей

Ранее мы посмотрели, как создавать указатели на типы значений, например, int или структуры. Однако кроме структур в C# есть еще и классы, которые в отличие от типов значений, помещают все связанные значения в куче. И в работу данных классов может в любой момент вмешаться сборщик мусора, периодически очищающий кучу. Чтобы фиксировать на все время работы указатели на объекты классов используется оператор fixed.

Допустим, у нас есть класс Point:


class Point
{
    public int x;
    public int y;
    public override string ToString() => $"x: {x}  y: {y}";
}

Зафиксируем указатель с помощью оператора fixed:


unsafe
{
    Point point = new Point();

    // блок фиксации указателя
    fixed (int* pX = &point.x)
    {
        *pX = 30;
    }
    fixed (int* pY = &point.y)
    {
        *pY = 150;
    }
    // можно совместить оба блока
    /*fixed (int* pX = &point.x, pY = &point.y)
    {
        *pX = 30;
        *pY = 150;
    }*/
    Console.WriteLine(point); // x: 30  y: 150
}

Оператор fixed создает блок, в котором фиксируется указатель на поле объекта person. После завершения блока fixed закрепление с переменных снимается, и они могут быть подвержены сборке мусора.

Кроме адреса переменной можно также инициализировать указатель, используя массив, строку или буфер фиксированного размера:


unsafe 
{
    int[] nums = { 0, 1, 2, 3, 7, 88 };
    string str = "Привет мир";
    fixed(int* p = nums)
    {
		int third = *(p+2);     // получим третий элемент
		Console.WriteLine(third); // 2
    }
    fixed(char* p = str)
    {
		char forth = *(p + 3);     // получим четвертый элемент
		Console.WriteLine(forth); // в
    }
}

При инициализации указателей на строку следует учитывать, что указатель должен иметь тип char*.

Проверь себя

📋 Тест по теме «Указатели на структуры, члены классов и массивы» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

_____
{
    Point point = new Point(0, 0);
    Console.WriteLine(point);   // X: 0  Y: 0
    Point* p = &point;

    p->X = 30;
    Console.WriteLine(p->X);    // 30

    // разыменовывание указателя
    (*p).Y = 180;
    Console.WriteLine((*p).Y);  // 180

    Console.WriteLine(point);   // X: 30  Y: 180
}
struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
    public Point(int x, int y)
    {
        X = x; Y = y;
    }
    public override string ToString() => $"X: {X}  Y: {Y}";
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

unsafe
{
    _____ int size = 7;
    int* square = stackalloc int[size]; // выделяем память в стеке под семь объектов int
    int* p = square;
    // вычисляем квадраты чисел от 1 до 7 включая
    for (int i = 1; i <= size; i++, p++)
    {
        // считаем квадрат числа
        *p = i * i;
    }
    for (int i = 0; i < size; i++)
    {
        Console.WriteLine(square[i]);
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

unsafe
{
    const int size = _____;
    int* factorial = stackalloc int[size]; // выделяем память в стеке под семь объектов int
    int* p = factorial;

    *(p++) = 1; // присваиваем первой ячейке значение 1 и
    // увеличиваем указатель на 1
    for (int i = 2; i <= size; i++, p++)
    {
        // считаем факториал числа
        *p = p[-1] * i;
    }
    for (int i = 0; i < size; i++)
    {
        Console.WriteLine(factorial[i]);
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

*p= _____;
p++;
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

unsafe
{
    Point point = new Point(_____, 0);
    Console.WriteLine(point);   // X: 0  Y: 0
    Point* p = &point;

    p->X = _____;
    Console.WriteLine(p->X);    // 30

    // разыменовывание указателя
    (*p).Y = 180;
    Console.WriteLine((*p).Y);  // 180

    Console.WriteLine(point);   // X: 30  Y: 180
}
struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
    public Point(int x, int y)
    {
        X = x; Y = y;
    }
    public override string ToString() => $"X: {X}  Y: {Y}";
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

unsafe
{
    const int size = 7;
    int* square = _____ int[size]; // выделяем память в стеке под семь объектов int
    int* p = square;
    // вычисляем квадраты чисел от 1 до 7 включая
    for (int i = 1; i <= size; i++, p++)
    {
        // считаем квадрат числа
        *p = i * i;
    }
    for (int i = 0; i < size; i++)
    {
        Console._____(square[i]);
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

Строки кода из примера этого урока перемешаны. Впишите их в правильном порядке, каждую строку — с новой строки.

class Point
    public int x;
{
    public override string ToString() => $"x: {x}  y: {y}";
    public int y;
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Указатели на структуры, члены классов и массивы»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «Указатели на структуры, члены классов и массивы», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

unsafe
{
    Point point = new Point(0, 0);
    Console.WriteLine(point);   // X: 0  Y: 0
    Point* p = &point;

    p->X = 30;
    Console.WriteLine(p->X);    // 30

    // разыменовывание указателя
    (*p).Y = 180;
    Console.WriteLine((*p).Y);  // 180

    Console.WriteLine(point);   // X: 30  Y: 180
}
struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
    public Point(int x, int y)
    {
        X = x; Y = y;
    }
    public override string ToString() => $"X: {X}  Y: {Y}";
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Указатели на структуры, члены классов и массивы»
+30 XP

Изучите приведённый фрагмент кода и опишите, при каких входных данных или условиях он может сработать не так, как ожидается (граничный случай, отсутствие проверки и т.п.). Предложите исправленный вариант.

unsafe
{
    const int size = 7;
    int* square = stackalloc int[size]; // выделяем память в стеке под семь объектов int
    int* p = square;
    // вычисляем квадраты чисел от 1 до 7 включая
    for (int i = 1; i <= size; i++, p++)
    {
        // считаем квадрат числа
        *p = i * i;
    }
    for (int i = 0; i < size; i++)
    {
        Console.WriteLine(square[i]);
    }
}
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Указатели на структуры, члены классов и массивы»
+50 XP

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

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «Указатели на структуры, члены классов и массивы»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «Указатели на структуры, члены классов и массивы». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.