Подтвердить что ты не робот

Пытаясь использовать free(), чтобы понять, как это работает

Чтобы понять использование бесплатного языка программирования C, я попытался запустить этот код на Ubuntu, но при запуске EXE файла я получаю ошибку SIGABRT. Почему программа не выходила нормально?

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

int main()
{
   int ret;
   int *ptr;
   ptr = (int *)malloc(sizeof(int)*10);
   free(ptr);
   ptr = &ret;
   free(ptr);
   return 0;
}
4b9b3361

Ответ 1

Попытка освободить указатель, который вы не получили от malloc (или одного из его друзей), вызывает поведение undefined. Ваш второй вызов free(ptr) пытается это сделать.

Из спецификации C, §7.22.3.3 Функция free, пункт 2:

Функция free приводит к тому, что пространство, на которое указывает ptr, освобождается, то есть становится доступным для дальнейшего выделения. Если ptr - пустой указатель, никаких действий не происходит. В противном случае, если аргумент не соответствует указателю, ранее возвращенному функцией управления памятью, или если пространство было освобождено вызовом free или realloc, поведение undefined.

Ответ 2

   ptr = &ret;
   free(ptr);

совпадает с:

    free(&ret);

ret находится в стеке, а не в куче. Попытка free() памяти, которая не была выделена (с malloc() и т.д.) В куче, приведет к поведению undefined.

Ответ 3

В то время как все ответы на поведение undefined правильны, просмотр реализации может быть более полезным.

Я думаю, что очень хорошая ссылка на это K & R C malloc. Объяснение того, как это работает, находится в "Программирование Langugage" , глава 8.7.

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

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

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

Также можно рассмотреть другие реализации malloc (это неполный список библиотек распределения).

Ответ 4

Функция free() работает только для памяти, которая была выделена на heap на malloc(). Не для статического распределения, потому что статические распределения автоматически обрабатываются. Вот ошибка:

 int ret; 

 ptr = &ret;

 free(ptr);

Вы не можете сделать это, потому что память для ret не выделяется в куче. Это в стеке, и только память кучи должна быть освобождена.

Ответ 5

Второй free(ptr) вызывает поведение undefined, поскольку вы пытаетесь освободить указатель, который вы не выделили. Также обратите внимание, что статические распределения автоматически исправляются, поэтому вы не требуете от них освобождения.

Ответ 6

  ptr = &ret;
   free(ptr);

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

Свободно работает только с памятью выделения кучи.

Ответ 7

Существуют три области, в которых переменные могут быть созданы в программе c или С++.

  • глобальные или статические переменные находятся в фиксированных местах исполняемого двоичного файла.
  • автоматические переменные за пределами статической области находятся в стеке
  • переменные malloc'ed или calloc'ed находятся в куче.

free() - это функция, которая освобождает ранее выделенную память в куче. Указатель на память в куче возвращается malloc или аналогичными функциями. Единственный способ прочитать или записать эту память через указатель. Указатель - это адрес, а указатель * - это содержимое, указанное на этом адресе.

В показанном примере есть две переменные, определенные в Main, и эффективно статические, и возвращаемое значение Main, которое находится в стеке. Используя miniumum int, 16 бит, вот возможная карта памяти. На этой карте инструкции начинаются с 0, стек начинается с некоторого ненулевого значения (начало стека - бос) и растет с увеличением, а куча начинается с максимального адреса (... FFFF, aka -1) и растет с помощью decrememeting:

(Помните, что MIN_INT - -32768, MAX_INT - 32767... спецификация гарантирует только 16 бит, подписанный)

Каждый байт имеет адрес шириной 'n' - 16, 32 или 64 бита, обычно


-1. (начало кучи, например, 16-битный адр: 0xFFFF, 32-битный адр: 0xFFFFFFFF или 64-битный адр: 0xFFFFFFFFFFFFFFFF)

-2. (1-е место вниз от начала кучи 0x... FFFE) ptr [9], за один раз

-3. (2-е место вниз от начала кучи 0x... FFFD)

-4. (3-е место вниз от начала кучи 0x... FFFC) ptr [8], за один раз

[надрез]

-17. (16-е место с начала кучи 0x... FFEF)

-18. (17-е место вниз от начала кучи 0x... FFEE) ptr [1] за один раз

-19. (18-е место с начала кучи 0x... FFED)

-20 (положение 19 вниз с начала кучи 0x... FFEC) ptr [0], за один раз

-21. (верхняя часть кучи, 10 X 16 бит int с начала кучи 0x... FFEB), за один раз

: Очень большой диапазон адресов на 32 или 64-битных машинах...

tos: (Top of stack 0x... tos)

bos + (sizeof (int) - 1) Конец int возвращается из Main()

bos: (начало стека: выше статических данных) Начало int, возвращаемое из Mail()

togs: (верхняя часть глобальной/статической) Конец "ptr"

: (размер указателя - ширина адресной шины... все, что требуется)

togs- (n-1): (top of global/static - (sizeof (int *) - 1)) начало "ptr"

(togs-n): Конец "ret"

(togs-n) -1: начало "ret"

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

(конец программного кода)

(начало программного кода)

(верх непрограммного кода)

0 (начало непрограммного кода, 0x... 0000)


Во время выполнения "ptr" и "ret" , вероятно, начинаются с "0", так как они фиксированные, статические, значения, считанные из файла, из которого исходит исполняемый двоичный файл.

Когда программа запускается, значение "ptr" изменяется, прежде всего, на кучу, в массиве malloc'ed из 10 ints: "0x... FFEC"

Призыв free() не изменяет значение ptr, его по-прежнему "0x... FFEC" Free'ing "0x... FFEC" является законным и работает без каких-либо смешных событий.

Назначение "ptr = & ret" устанавливает новое значение в "ptr" , "(togs-n) -1" , начало "ret" .

Free'ing "(togs-n) -1" вызывает немедленный сбой, потому что "free" проверяет значение "(togs-n) -1" и НЕ находится в допустимом диапазоне для адреса кучи.

"ret" остается пустым, его никогда не устанавливают, но поскольку его глобальный/статический, он остается тем, что было, когда компоновщик написал его на диск.