---
title: Память, указатели и управление ресурсами
seo:
  description: >-
    Тема «Память, указатели и управление ресурсами» для собеседования C++
    Developer. В чем разница между указателем и ссылкой? Для чего нужен
    указатель на функцию? Как его объявить?
  title: Память, указатели и управление ресурсами — C++ Developer
---

[Все темы C++ Developer](/prep/c-developer)

Подтемы:

- [Память и указатели](#q-14bee738d69b81c1bc98db4a99b620c7)
- [Память](#q-14bee738d69b818cb0e6f8336eddfa57)
- [Указатели](#q-14bee738d69b812b947bf245f3ebffa9)
- [Управление памятью](#q-14bee738d69b818c8cabf6a269c27e12)

## <strong>В чем разница между указателем и ссылкой?</strong> [#q-14bee738d69b81c1bc98db4a99b620c7]

Основные различия между указателем и ссылкой в C++&#58;

{/* prettier-ignore */}
1. <strong>Синтаксис и использование</strong>&#58;

    - <strong>Указатель</strong>&#58; Переменная, которая хранит адрес другой переменной.

        ```cpp
        int a = 10;
        int* ptr = &a;
        ```

    - <strong>Ссылка</strong>&#58; Альтернативное имя для уже существующей переменной.

        ```cpp
        int a = 10;
        int& ref = a;
        ```

1. <strong>Инициализация</strong>&#58;

    - <strong>Указатель</strong>&#58; Может быть инициализирован позже или изменён для указания на другой адрес.

        ```cpp
        int b = 20;
        ptr = &b;  // Теперь ptr указывает на b
        ```

    - <strong>Ссылка</strong>&#58; Должна быть инициализирована сразу при создании и не может быть изменена для ссылок на другой объект.

        ```cpp
        int c = 20;
        ref = c;  // Изменяет значение a на 20, но ref по-прежнему ссылается на a
        ```

1. <strong>Нулевое значение</strong>&#58;

    - <strong>Указатель</strong>&#58; Может быть нулевым (<code>nullptr</code>), что означает, что он не указывает ни на какой объект.

        ```cpp
        int* ptr = nullptr;
        ```

    - Ссылку нужно связать с подходящим объектом или функцией. Сам язык не отслеживает время жизни цели&#58; после её уничтожения ссылка может стать висячей.

1. <strong>Операции разыменования</strong>&#58;

    - <strong>Указатель</strong>&#58; Требует операцию разыменования (<code>&#42;</code>) для доступа к значению, на которое указывает.

        ```cpp
        int value = *ptr;
        ```

    - <strong>Ссылка</strong>&#58; Не требует специальной операции для доступа к значению.

        ```cpp
        int value = ref;
        ```

Указатель можно переназначить и проверить на nullptr. Ссылка удобна как обязательный параметр и не переназначается, но не гарантирует время жизни объекта. В обоих случаях нужно избегать висячих обращений.

:::note[Ссылки для изучения]

1. [Ссылки. Указатели. Константность](https://education.yandex.ru/handbook/cpp/article/references-pointers-const)
2. [Указатели и ссылки C++. #SimpleCode, с 0:08](https://www.youtube.com/watch?v=hZOX-LlUETE&t=8s)

:::

---

## <strong>Для чего нужен указатель на функцию? Как его объявить?</strong> [#q-14bee738d69b81e6bbe3f7c6f01b4e1e]

Указатель на функцию используется для хранения адреса функции и позволяет вызывать функции через этот указатель. Это полезно для реализации обратных вызовов (callbacks), таблиц виртуальных функций и гибких механизмов обработки событий.

#### Объявление указателя на функцию&#58;

{/* prettier-ignore */}
1. <strong>Объявление</strong>&#58;

    - Для функции с прототипом <code>void foo&#40;int&#41;</code>&#58;

        ```cpp
        void (*funcPtr)(int);
        ```

1. <strong>Инициализация</strong>&#58;

    - Присвоение адреса функции указателю&#58;

        ```cpp
        funcPtr = foo;
        ```

1. <strong>Использование</strong>&#58;

    - Вызов функции через указатель&#58;

        ```cpp
        funcPtr(5);  // Вызывает foo(5)
        ```

Пример&#58;

```cpp
#include <iostream>// Обычная функция
void foo(int x) {
    std::cout << "Value: " << x << std::endl;
}

int main() {
    // Объявление указателя на функцию
    void (*funcPtr)(int);

    // Инициализация указателя адресом функции foo
    funcPtr = foo;

    // Вызов функции через указатель
    funcPtr(5);

    return 0;
}
```

:::note[Ссылки для изучения]

1. [Объявление и вызов указателя на функцию C++](https://metanit.com/cpp/tutorial/4.8.php)
   :::

---

## <strong>Что будет, если забыть вызвать</strong> <code>delete</code><strong>? Когда освободится та память?</strong> [#q-14bee738d69b81058d8bca82b37832a2]

Если забыть вызвать <code>delete</code> для динамически выделенной памяти, произойдёт утечка памяти <code>&#40;memory leak&#41;</code>. В таком случае&#58;

- Память, выделенная с помощью <code>new</code>, не будет освобождена.

- Память будет занята до завершения программы.

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

Память, не освобождённая через <code>delete</code>, будет освобождена операционной системой при завершении программы, но в процессе выполнения программы эта память останется недоступной для повторного использования.

:::note[Ссылки для изучения]

1. [Утечки динамической памяти и владение ресурсом через RAII](https://education.yandex.ru/handbook/cpp/article/raii-and-smart-pointers)
   :::

---

## <strong>Что такое умный указатель? Какие умные указатели есть в стандартной библиотеке?</strong> [#q-14bee738d69b813880eee005470557e1]

Умный указатель хранит указатель и реализует правила владения. unique_ptr освобождает ресурс при уничтожении или reset, shared_ptr при исчезновении последнего владельца. weak_ptr не владеет объектом и сам его не освобождает.

1. <code>std&#58;&#58;unique&#95;ptr</code>&#58; Эксклюзивное владение ресурсом.
   Не может быть скопирован, только перемещён.

1. <code>std&#58;&#58;shared&#95;ptr</code>&#58; Совместное владение ресурсом.
   Поддерживает подсчёт ссылок, освобождает память, когда последний
   <code>shared&#95;ptr</code> выходит из области видимости.

1. <code>std&#58;&#58;weak&#95;ptr</code>&#58; Неблокирующая ссылка на ресурс,
   управляемый <code>std&#58;&#58;shared&#95;ptr</code>. Не увеличивает подсчёт
   ссылок.

Пример&#58;

```cpp
#include <memory>

std::unique_ptr<int> ptr1(new int(10));
std::shared_ptr<int> ptr2 = std::make_shared<int>(20);
std::weak_ptr<int> ptr3 = ptr2;
```

:::note[Ссылки для изучения]

1. [RAII, unique_ptr и shared_ptr в C++](https://education.yandex.ru/handbook/cpp/article/raii-and-smart-pointers)
   :::

---

## Как работает const для переменных, ссылок и указателей и от чего зависит размер указателя? [#q-14bee738d69b8165be4efb4c60b3dc73]

<strong>Константность переменной, ссылки, указателя&#58;</strong>

- <strong>Константная переменная</strong>&#58; Значение не может быть изменено
  после инициализации.

  ```cpp
  const int a = 10;
  ```

- <strong>Константная ссылка</strong>&#58; Ссылка на константу, через которую
  нельзя изменить значение.

  ```cpp
  const int& ref = a;
  ```

- <strong>Константный указатель</strong>&#58; Указатель, адрес которого нельзя
  изменить после инициализации.

  ```cpp
  int value = 10;
  int* const ptr = &value;
  ```

- <strong>Указатель на константу</strong>&#58; Указатель на значение, которое
  нельзя изменить через этот указатель.

  ```cpp
  const int* ptr = &a;
  ```

<strong>Константный указатель и указатель на константу&#58;</strong>

- <strong>Константный указатель (</strong>
  <code>int&#42; const</code>
  <strong>)</strong>&#58; Указатель не может указывать на другой адрес, но
  данные по этому адресу могут изменяться.

  ```cpp
  int value = 10;
  int* const ptr = &value;
  ```

- <strong>Указатель на константу (</strong>
  <code>const int&#42;</code>
  <strong>)</strong>&#58; Указатель может менять адрес, но данные по адресу
  менять нельзя.

  ```cpp
  const int* ptr = &a;
  ```

<strong>Размер указателя&#58;</strong>

- Размер указателя определяется типом указателя и реализацией; проверяйте sizeof. Следующие размеры типичны для распространённых ABI, но не гарантированы стандартом для всех платформ и указателей на члены.
  - <strong>32-битная система</strong>&#58; Указатель занимает 4 байта.

  - <strong>64-битная система</strong>&#58; Указатель занимает 8 байт.

:::note[Ссылки для изучения]

1. [Ссылки. Указатели. Константность](https://education.yandex.ru/handbook/cpp/article/references-pointers-const)
   :::

---

## <strong>Что случится, если вернуть ссылку на временный объект?</strong> [#q-14bee738d69b819a87f6ed9bbd2ac281]

Если вернуть ссылку на временный объект, то после завершения функции объект будет уничтожен, и ссылка станет <strong>"висячей"</strong> <code>&#40;dangling reference&#41;</code>. Использование такой ссылки приведёт к неопределённому поведению, что может вызвать ошибки или сбои программы.

:::note[Ссылки для изучения]

1. [Ссылки. Указатели. Константность](https://education.yandex.ru/handbook/cpp/article/references-pointers-const)
   :::

---

## <strong>Где может храниться переменная?</strong> [#q-14bee738d69b818cb0e6f8336eddfa57]

В языке программирования C переменные могут храниться в различных областях памяти, каждая из которых имеет свои особенности по времени жизни и области видимости. Вот основные места хранения переменных&#58;

{/* prettier-ignore */}
1. <strong>Стек</strong> <code>&#40;Stack&#41;</code>&#58; Локальные переменные внутри функций обычно хранятся на стеке. Они создаются при вызове функции и уничтожаются при её завершении.

1. <strong>Куча</strong> <code>&#40;Heap&#41;</code>&#58; Динамически выделенная память (через <code>malloc</code>, <code>calloc</code>, <code>realloc</code> и другие функции) находится в куче. Переменные в куче сохраняются до тех пор, пока не будут освобождены с помощью <code>free</code>.

1. <strong>Глобальная память</strong> <code>&#40;Global Area&#41;</code>&#58; Глобальные и статические переменные хранятся в глобальной области памяти. Они инициализируются при запуске программы и существуют до её завершения.

1. <strong>Регистры</strong> <code>&#40;Registers&#41;</code>&#58; Компилятор может оптимизировать программу, помещая некоторые переменные непосредственно в регистры процессора для быстрого доступа.

1. <strong>Литеральный пул</strong> <code>&#40;Literal Pool&#41;</code>&#58; Константы, такие как строковые литералы, обычно хранятся в отдельной области памяти, оптимизированной для неизменяемых данных.

:::note[Ссылки для изучения]

1. [Стековое время жизни, динамические объекты и static в C++](https://education.yandex.ru/handbook/cpp/article/object-lifetime)
   :::

---

## <strong>Какая разница между</strong> <code>calloc</code> <strong>и</strong> <code>malloc</code><strong>?</strong> [#q-14bee738d69b819fbcb8c0d7954f9f9d]

Функции <code>calloc</code> и <code>malloc</code> используются в языке программирования C для динамического выделения памяти, но они работают немного по-разному&#58;

{/* prettier-ignore */}
1. <strong>malloc (Memory Allocation)</strong>&#58;

    - <strong>Принцип работы</strong>&#58; Выделяет блок памяти заданного размера.

    - <strong>Инициализация</strong>&#58; Память, выделенная с помощью <code>malloc</code>, не инициализируется. Это означает, что начальные значения в выделенной памяти не определены (могут быть мусорные значения).

    - <strong>Синтаксис</strong>&#58; <code>void&#42; malloc&#40;size&#95;t size&#41;&#59;</code>

    - <strong>Пример</strong>&#58; <code>int&#42; ptr &#61; malloc&#40;10 &#42; sizeof&#40;int&#41;&#41;&#59;</code> выделит память для 10 целочисленных значений, но не инициализирует их.

1. <strong>calloc (Contiguous Allocation)</strong>&#58;

    - <strong>Принцип работы</strong>&#58; Выделяет блок памяти для массива элементов заданного размера и количества.

    - <strong>Инициализация</strong>&#58; Память, выделенная с помощью <code>calloc</code>, автоматически инициализируется нулями.

    - <strong>Синтаксис</strong>&#58; <code>void&#42; calloc&#40;size&#95;t num&#44; size&#95;t size&#41;&#59;</code>

    - <strong>Пример</strong>&#58; <code>int&#42; ptr &#61; calloc&#40;10&#44; sizeof&#40;int&#41;&#41;&#59;</code> выделит память для 10 целочисленных значений и инициализирует их нулями.

Основное отличие между <code>malloc</code> и <code>calloc</code> заключается в инициализации&#58; <code>calloc</code> инициализирует выделенную память нулями, в то время как <code>malloc</code> оставляет память неинициализированной.

:::note[Ссылки для изучения]

1. [malloc в библиотеке времени выполнения C++](https://learn.microsoft.com/ru-ru/cpp/c-runtime-library/reference/malloc?view=msvc-170)
1. [calloc: выделение массива с нулевым заполнением](https://learn.microsoft.com/ru-ru/cpp/c-runtime-library/reference/calloc?view=msvc-170)
   :::

---

## <strong>Что такое указатель?</strong> [#q-14bee738d69b812b947bf245f3ebffa9]

Указатель в программировании, особенно в языках C и C++, — это переменная, которая хранит адрес памяти другой переменной. Указатели используются для следующих целей&#58;

1. <strong>Доступ к данным</strong>&#58; Указатель позволяет прямой доступ к
   данным, на которые он указывает, что может использоваться для изменения этих
   данных.

1. <strong>Управление памятью</strong>&#58; С помощью указателей можно
   динамически выделять и освобождать память во время выполнения программы.

1. <strong>Работа с массивами и строками</strong>&#58; Указатели обеспечивают
   эффективный способ обработки массивов и строк.

1. <strong>Передача аргументов по ссылке</strong>&#58; Указатели позволяют
   функциям изменять значения переданных переменных и работать с большими
   структурами данных без значительных затрат на копирование.

1. <strong>Создание сложных структур данных</strong>&#58; Указатели используются
   для создания связанных структур данных, таких как списки, деревья и графы.

Указатель определяется в C с использованием символа звездочки <code>\*</code>, а доступ к значению, на которое он указывает, осуществляется через оператор разыменования <code>\*</code>. Получение адреса переменной для сохранения в указателе выполняется с помощью оператора адреса <code>&#38;</code>.

:::note[Ссылки для изучения]

1. [Указатели и ссылки в C++: адрес, разыменование и nullptr](https://education.yandex.ru/handbook/cpp/article/references-pointers-const)
2. [Указатели и ссылки C++. #SimpleCode, с 0:08](https://www.youtube.com/watch?v=hZOX-LlUETE&t=8s)

:::

---

## <strong>Какой размер имеет указатель и от чего он зависит?</strong> [#q-14bee738d69b81ceae57e8fd2d6630f4]

Размер указателя в языках программирования, таких как C и C++, зависит от архитектуры системы, на которой выполняется программа. Обычно размер указателя равен&#58;

- <strong>32 бита (4 байта)</strong> на 32-битных системах.

- <strong>64 бита (8 байт)</strong> на 64-битных системах.

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

:::note[Ссылки для изучения]

1. [Почему размер указателя на метод C++ зависит от реализации](https://habr.com/ru/articles/333334/)
   :::

---

## <strong>Какие есть операции с указателями?</strong> [#q-14bee738d69b8188ad14fc48a704dcd6]

Операции с указателями в языках программирования, таких как C и C++, позволяют манипулировать адресами и данными, на которые указывают эти адреса. Вот основные операции, которые можно выполнять с указателями&#58;

1. <strong>Присваивание</strong>&#58; Установка адреса переменной в указатель.

   ```cpp
   int a = 10;
   int* ptr = &a;  // Указатель ptr теперь указывает на a.
   ```

1. <strong>Разыменование</strong>&#58; Доступ к данным по адресу, хранящемуся в
   указателе.

   ```cpp
   int value = *ptr;  // value теперь равно 10, значение a.
   ```

1. Сдвиг указателя&#58; Прибавление целого n смещает указатель на n элементов в пределах одного массива, включая позицию сразу после конца. Разыменовывать позицию после конца нельзя.

   ```cpp
   ptr++;  // Перемещение на следующий адрес, типично для типа данных (например, на 4 байта вперед для int во многих системах).
   ```

1. <strong>Сравнение</strong>&#58; Сравнение адресов, хранящихся в указателях.

   ```cpp
   if (ptr1 == ptr2)  // Проверка, указывают ли два указателя на одно и то же место в памяти.
   ```

1. <strong>Арифметика указателей</strong>&#58; Вычисление разницы между двумя
   указателями, что дает количество элементов между ними.

   ```cpp
   int* ptr1 = &a[10];
   int* ptr2 = &a[5];
   int n = ptr1 - ptr2;  // n будет равно 5, если ptr1 и ptr2 указывают на элементы одного массива.
   ```

1. <strong>Получение адреса</strong>&#58; Использование оператора
   <code>&#38;</code> для получения адреса переменной.

   ```cpp
   int* ptr = &a;  // Получение адреса переменной a.
   ```

:::note[Ссылки для изучения]

1. [Указатели и ссылки в C++: адрес, разыменование и nullptr](https://education.yandex.ru/handbook/cpp/article/references-pointers-const)
   :::

---

## <strong>Чем отличаются</strong> <code>delete</code> <strong>и</strong> <code>delete&#91;&#93;</code><strong>? Что случится, если вызвать</strong> <code>delete</code> <strong>у объекта, созданного через</strong> <code>new&#91;&#93;</code><strong>?</strong> [#q-14bee738d69b818c8cabf6a269c27e12]

<strong>Отличия</strong> <code>delete</code> <strong>и</strong>
<code>delete&#91;&#93;</code>
<strong>&#58;</strong>

- <code>delete</code>&#58; Используется для освобождения памяти, выделенной для
  одиночного объекта с помощью <code>new</code>.

  ```cpp
  int* p = new int;
  delete p;
  ```

- <code>delete&#91;&#93;</code>&#58; Используется для освобождения памяти,
  выделенной для массива объектов с помощью <code>new&#91;&#93;</code>.

  ```cpp
  int* arr = new int[10];
  delete[] arr;
  ```

<strong>Использование</strong> <code>delete</code> <strong>вместо</strong>
<code>delete&#91;&#93;</code> <strong>для объекта, созданного через</strong>
<code>new&#91;&#93;</code>
<strong>, приводит к неопределённому поведению. Это может вызвать&#58;</strong>

- Неполное освобождение памяти (утечка памяти).

- Потенциальные ошибки выполнения или сбои программы.

#### Рекомендация&#58;

Всегда использовать <code>delete&#91;&#93;</code> для освобождения памяти, выделенной с помощью <code>new&#91;&#93;</code>, чтобы избежать неопределённого поведения и корректно освободить всю выделенную память.

:::note[Ссылки для изучения]

1. [delete, new\[\] в C++ и городские легенды об их сочетании](https://habr.com/ru/companies/contentai/articles/117208/)
   :::

---

## <strong>Что такое</strong> <code>memory leak</code><strong>?</strong> [#q-14bee738d69b815f81bec733a1e5292b]

<strong>Memory leak (утечка памяти)</strong> — это ситуация, когда программа
выделяет память динамически (например, с помощью <code>new</code> или
<code>malloc</code>), но не освобождает её (с помощью <code>delete</code> или
<code>free</code>). В результате, используемая память остаётся занятой и
недоступной для повторного использования, что может привести к исчерпанию
доступной памяти и замедлению или краху программы.

:::note[Ссылки для изучения]

1. [Утечки динамической памяти и владение ресурсом через RAII](https://education.yandex.ru/handbook/cpp/article/raii-and-smart-pointers)
   :::
