← Назад к вопросам

На какую область памяти будет ссылаться указатель после вызова malloc: на кучу или стек

1.7 Middle🔥 191 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

malloc выделяет память в куче, а не на стеке

Короткий ответ

malloc выделяет память в куче (heap), НЕ на стеке (stack). Сам указатель, который возвращает malloc, часто может быть расположен на стеке, но данные, на которые он ссылается, всегда находятся в куче.

Различие между стеком и кучей

Стек (Stack):

  • Линейная структура памяти (LIFO — Last In, First Out)
  • Автоматическое управление: переменные освобождаются при выходе из области видимости
  • Быстро: просто сдвигаем указатель стека
  • Ограниченный размер (обычно 1-8 МБ на поток)
  • Использование: локальные переменные, параметры функций, адреса возврата

Куча (Heap):

  • Свободная структура памяти
  • Ручное управление: нужно вызвать free() для освобождения
  • Медленнее: нужна фрагментация и поиск свободного блока
  • Неограниченный размер (до размера RAM + swap)
  • Использование: объекты переменного размера, данные, переживающие вызов функции

Пример: где находятся указатель и данные

#include <stdlib.h>
#include <stdio.h>

int main() {
    // ptr находится НА СТЕКЕ
    int* ptr = malloc(sizeof(int) * 10);
    //    ↑                           ↑
    //  Стек                       Куча
    
    // Используем память в куче через указатель со стека
    ptr[0] = 42;
    ptr[5] = 100;
    
    printf("Адрес указателя (на стеке): %p\n", &ptr);
    printf("Адрес данных (в куче): %p\n", (void*)ptr);
    printf("Первое значение: %d\n", ptr[0]);
    
    // ВАЖНО: освобождаем память в куче
    free(ptr);
    
    return 0;
}

/* Примерный вывод:
Адрес указателя (на стеке): 0x7ffc5fbff8c8
Адрес данных (в куче): 0x55a0a0e45260
Первое значение: 42
*/

Обратите внимание: адреса сильно отличаются! Это потому что они в разных областях памяти.

Визуализация памяти процесса

Высокие адреса:
┌─────────────────────────┐
│   Окружение (envp)      │
├─────────────────────────┤
│   Аргументы (argv)      │
├─────────────────────────┤
│   STACK                 │ ← Стек растёт вниз
│  (локальные переменные, │  Быстро и автоматично
│   указатели, параметры) │
│         ...             │
├─────────────────────────┤
│      (пусто)            │
├─────────────────────────┤
│   HEAP                  │ ← Куча растёт вверх
│  (malloc, calloc, new)  │  Медленно, ручное управление
│         ...             │
├─────────────────────────┤
│   BSS Segment           │
│  (глобальные данные)    │
├─────────────────────────┤
│   Data Segment          │
│  (инициализированные    │
│   глобальные переменные)│
├─────────────────────────┤
│   Text Segment (Code)   │
│  (исполняемый код)      │
Низкие адреса

Пример с визуализацией

#include <stdio.h>
#include <stdlib.h>

int global_var = 100;           // Data Segment
int static_arr[100];            // BSS Segment

void print_addresses() {
    int stack_var = 42;         // Stack
    int* heap_ptr = malloc(sizeof(int) * 5);  // ptr на Stack, данные на Heap
    
    printf("Адреса:\n");
    printf("Code (функция): %p\n", (void*)print_addresses);
    printf("Data (global_var): %p\n", (void*)&global_var);
    printf("BSS (static_arr): %p\n", (void*)static_arr);
    printf("Stack (stack_var): %p\n", (void*)&stack_var);
    printf("Heap (данные): %p\n", (void*)heap_ptr);
    
    free(heap_ptr);
}

int main() {
    print_addresses();
    return 0;
}

/* Примерный вывод (адреса упорядочены):
Адреса:
Code (функция): 0x55a0a01e6149
Data (global_var): 0x55a0a0207010
BSS (static_arr): 0x55a0a0207014
Heap (данные): 0x55a0a0e45260
Stack (stack_var): 0x7ffc5fbff8c8

Обратите внимание на огромный перепад между Heap и Stack!
*/

Почему malloc выделяет ИМЕННО в куче

  1. Размер неизвестен на этапе компиляции
    • Стек имеет фиксированный размер
    • Куча может расти динамически
int size;
scanf("%d", &size);
int* arr = malloc(size * sizeof(int));  // Размер известен только в runtime
  1. Время жизни данных
    • Стек автоматически очищается при выходе из функции
    • malloc данные остаются, пока не вызовешь free()
int* create_array() {
    int stack_arr[10];      // Исчезнет при выходе из функции!
    
    int* heap_arr = malloc(10 * sizeof(int));  // Останется!
    return heap_arr;  // heap_arr можно возвращать
    // stack_arr вернуть нельзя — будет ошибка!
}
  1. Обход ограничений стека
    • Для больших объёмов данных стека недостаточно
    • Куча может быть намного больше
int huge_array[1000000];    // ❌ Stack overflow!
int* huge_array = malloc(1000000 * sizeof(int));  // ✅ Работает

Частые ошибки

// ❌ ОШИБКА 1: Вернуть указатель на стек
int* bad_ptr() {
    int local = 42;
    return &local;  // ОПАСНО! local исчезнет
}

// ✅ ПРАВИЛЬНО: Вернуть указатель на кучу
int* good_ptr() {
    int* ptr = malloc(sizeof(int));
    *ptr = 42;
    return ptr;  // Безопасно! Данные в куче
}

// ❌ ОШИБКА 2: Забыть освободить память
int main() {
    int* ptr = malloc(1000);
    // ... используем ptr ...
    // Забыли free(ptr);  → Memory leak!
}

// ✅ ПРАВИЛЬНО: Освободить память
int main() {
    int* ptr = malloc(1000);
    // ... используем ptr ...
    free(ptr);
    ptr = NULL;  // Хорошая практика: обнулить указатель
}

В C++: new и delete

В C++ ситуация аналогична, просто вместо malloc/free используют new/delete:

int* ptr = new int(42);     // Выделение в куче
delete ptr;                  // Освобождение из кучи

int arr[100];               // Выделение на стеке (автоматическое)
// Освобождение автоматическое при выходе из scope

int* arr = new int[100];    // Выделение в куче
delete[] arr;               // Освобождение из кучи ([] для массивов)

Резюме

  • malloc ВСЕГДА выделяет в куче
  • Сам указатель может быть на стеке, но данные — в куче
  • Куча требует ручного управления памятью (free/delete)
  • Стек автоматический, но ограниченный
  • malloc нужен для данных переменного размера и длительного времени жизни