Как free() влияет на адрес памяти в куче?

Это назначение просит нас выделить две переменные типа int с помощью malloc() (называемые var1 и var2), распечатать адреса каждой переменной (адрес указателя в стеке и адрес в куче), а затем использовать free() чтобы освободить var1, снова напечатайте адреса, затем выделите еще одно место в куче для var1 и напечатайте адреса в третий раз. Я считаю, что инструктор пытается показать нам, что адрес кучи для var1 должен измениться, но он всегда остается прежним... если я не удаляю free(var1) из кода. Инструктор сделал аналогичную демонстрацию, но не использовал free() для освобождения каких-либо переменных, поэтому мы никогда не видели, как это должно работать.

Вот мой код:

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

void main()
{

int *var1 = (int*)malloc(sizeof(int)); 
*var1 = 1000;                   
int *var2 = (int*)malloc(sizeof(int)); 
*var2 = 2000;

printf("Addresses of var1\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var1, var1); 
printf("Addresses of var2\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var2, var2);

free(var1);

printf("AFTER DEALLOCATING var1 FROM THE HEAP\n");
printf("Addresses of var1\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var1, var1); 
printf("Addresses of var2\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var2, var2);

var1 = (int*) malloc(sizeof(int));
*var1 = 1500;

printf("NEW MEMORY ADDRESS ALLOCATED FOR var1\n");
printf("Addresses of var1\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var1, var1); 
printf("Addresses of var2\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var2, var2); 

}

Этот код приводит к таким выводам:

Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

AFTER DEALLOCATING var1 FROM THE HEAP
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

NEW MEMORY ADDRESS ALLOCATED FOR var1
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

Как вы можете видеть, адрес кучи не изменяется для var1 когда я освобождаю его, и он не меняется, когда я снова var1 пространство памяти для var1. Однако, если я просто удаляю free(var1) строку free(var1) из программы, она просто назначает второе пространство памяти для var1 и указывает на это в куче, которая имеет другой адрес памяти:

Addresses of var1 
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

AFTER DEALLOCATING var1 FROM THE HEAP
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

NEW MEMORY ADDRESS ALLOCATED FOR var1
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000420

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

(Просто чтобы прояснить, все, что я сделал, это удалил free(var1) из предыдущего кода, поэтому раздел "AFTER DEALLOCATING var1" теперь показывает тот же адрес кучи, что и предыдущий набор, но он изменяет адрес кучи var1 в третий раздел.)

Кто-нибудь может сказать мне, что здесь происходит? Единственное логическое объяснение, которое я могу придумать, заключается в том, что когда я использую free() для освобождения var1 а затем печатаю адрес, он просто печатает LAST-адрес, на который он указал, а затем, когда я выделяю память для var1 во второй раз он просто "засыпает" предыдущий адрес новым значением var1. Имеет ли это смысл? У меня есть ошибки в моем коде, или это просто, как C ведет себя, когда освобождает память для переменной, а затем перераспределяет ее?

Ответ 1

Совершенно нормально, что malloc может возвращать те же адреса, когда память освобождается и затем перераспределяется. Также было бы нормально, чтобы он возвращал разные адреса.

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

Кстати:

  • void main() неверен. Должно быть int main(void).
  • Печать адреса после освобождения места, на которое он указывает, не поддерживается стандартом C. Это не редкость, когда он "работает", но это не правильно. C 2018 6.2.4 2 говорит нам: "Значение указателя становится неопределенным, когда объект, на который он указывает (или только что прошедший), достигает конца своего времени жизни". Когда объект, выделенный с помощью malloc, освобождается с помощью free, его время жизни заканчивается.

Ответ 2

Единственное логическое объяснение, которое я могу придумать, состоит в том, что когда я использую free() для освобождения var1, а затем печатаю адрес, он просто печатает последний адрес, на который он указал

Вид права. Освобождение указателя не влияет на содержимое указателя вообще. Или, если быть более точным, это значение является неопределенным после освобождения. На самом деле, в соответствии со стандартом, вы даже не можете доверять указателю, чтобы он содержал адрес, который он делал до вызова free. Насколько я знаю, это будет в большинстве случаев, но вы не можете доверять этому.

Стандарт гласит:

C 2018 6.2.4 2: "Значение указателя становится неопределенным, когда объект, на который он указывает (или только что прошедший), достигает конца своего времени жизни".

Распространенной ошибкой является использование теста if(ptr == NULL) в качестве проверки, правильно ли вы освободили указатель. Это не будет работать.

Ответ 3

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

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