Освобождение выделенной памяти

Можно ли освободить память, явно выделенную C в alloca(), до выхода из текущей функции? Если так, то как?

8 ответов

Решение

Это возможно, но не существует заранее написанной функции для этого. Вы должны были бы вникнуть в реализацию alloca() вашего компилятора, чтобы выяснить, что он делает, а затем написать свой собственный freea(). Так как каждый компилятор выполняет alloca() по-своему, вам придется переписать свой freea () для каждого компилятора.

Но мне трудно поверить, что это стоило того. Просто используйте malloc/free, если вам нужно явно освободить его - эти функции обычно сильно оптимизированы. Воспользуйтесь ими.

С http://www.gnu.org/software/libc/manual/html_mono/libc.html:

Выделение блока с alloca явное действие; Вы можете выделить столько блоков, сколько пожелаете, и вычислить размер во время выполнения. Но все блоки освобождаются при выходе из функции, из которой вызывается alloca, так же, как если бы они были автоматическими переменными, объявленными в этой функции. Нет возможности освободить пространство явно.

Используя C99, вы можете добиться того же, используя массив переменной длины. Просто объявите VLA в новой области; он будет автоматически освобожден при выходе из области.

Например:

int some_function(int n) {
    // n has the desired length of the array
    ...
    { // new scope
        int arr[n]; // instead of int *arr = alloca(n*sizeof(int));
        // do stuff with array
    }
    // function continues with arr deallocated
    ...
}

Вы размещаете в стеке с помощью alloca(); Если после этого что-то еще произошло (и вы не можете контролировать это, не записав все в сборку), вы не можете просто сжать стек обратно. Поэтому, пока вы не покинете фрейм стека своей функции, это невозможно.

Это также, почему вы можете действительно испортить ситуацию, если переполните выделенный буфер. Вы можете начать переписывать адреса кода, к которому возвращается ваша функция, заставляя ее прыгать куда-то еще, всякого рода ужасные вещи. Быть осторожен!

Malloc работает в куче, поэтому он гораздо более гибок в своих возможностях.

Это было бы полезно для продолжения прохождения стиля (CPS), а не реаллока.

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

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

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

#include <stdio.h>
#include <alloca.h>

int main()
{
  unsigned long p0, p1, p2;
  p0=(unsigned long)alloca(0);
  p1=(unsigned long)alloca((size_t) 0x1000);
  p2=(unsigned long)alloca((size_t)-0x1000);
  printf( "p0=%lX, p1=%lX, p2=%lX\n", p0, p1, p2 );
  return 0;
}

На старой машине x64 с clang 2.9 пример вывода:

p0=7FFF2C75B89F, p1=7FFF2C75A89F, p2=7FFF2C75B89F

Итак, мы знаем, что реализация не проверяет аргумент -0x1000, в противном случае значение без знака будет очень большим целым числом. Первоначально указатель стека был 0x...B89F; так как этот стек растет вверх (0x1000), поэтому измените указатель стека до (0x... B89F - 0x1000) = 0x... A89F. После отрицательного распределения (0xA89F - (-0x1000)) указатель стека вернулся к 0x...B89F.

Однако с gcc 4.8.3 пример вывода:

p0=7FFFA3E27A90, p1=7FFFA3E26A80, p2=7FFFA3E27A70

В /usr/include/alloca.h мы нашли:

#ifdef  __GNUC__
# define alloca(size)   __builtin_alloca (size)
#endif /* GCC.  */

Итак, мы знаем, что встроенная функция alloca, предоставляемая gcc 4.8.3, сделала то же самое, за исключением того, что она выделяла дополнительные 0x10 байтов в качестве запаса прочности. При выполнении отрицательного распределения все равно предполагается, что оно растет вверх и поэтому пытается зарезервировать 0x10 дополнительных байтов (- 0x10), поэтому p2= 0x...6A80 - (-0x1000) - 0x10 = 0x...7A70. Так что будьте особенно осторожны.

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

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

Вам не нужно писать никаких пользовательских вид функции, ни использовать VLA. Память, выделенная в стеке, может быть легко освобождена как в C, так и в C++ (C++ не поддерживает VLA). размещает в стеке, верно? Это означает, что память будет освобождена, когда она выйдет за пределы области видимости... так что просто используйте области видимости!

      #include <alloca.h>

int main()
{
    {
        void* ptr = alloca(1024);

        // do your stuff

    } // memory is deallocated here
    
    return 0;
}

Как видно из онлайн-компилятора godbolt, сборка (без оптимизации) работает правильно:https://godbolt.org/z/Gn5YMa

Другие вопросы по тегам