Другие языки программирования и технологии

Язык си, не могу понять динамическое выделение памяти.

#include
#include
#include
main()
{
int i, aSize;
int * randomNums;
time_t t;
double total = 0;
int biggest, smallest;
float average;
srand(time(&t));
printf("Сколько случайных чисел будет в массиве? ");
scanf(" %d", &aSize);
randomNums = (int *) malloc(aSize * sizeof(int));
// Проверка правильности выделения массива
if (!randomNums)
{
printf("Ошибка выделения массива случайных
чисел! \n");
exit(1);
}
// Проход по элементам массива и присвоение
//каждому элементу
// целого числа в диапазоне от 1 до 500
for (i = 0; i < aSize; i++)
{
randomNums[i] = (rand() % 500) + 1;
}
// Инициализация самого большого и самого
// маленького числа для последующего сравнения
biggest = 0;
smallest = 500;
// Проход по заполненному массиву и поиск
// наибольшего и наименьшего чисел, а также
// сложение всех чисел и вычисление среднего
for (i = 0; i < aSize; i++)
{
total += randomNums[i];
if (randomNums[i] > biggest)
{
biggest = randomNums[i];
}
if (randomNums[i] < smallest)
{
smallest = randomNums[i];
}
}
average = ((float)total)/((float)aSize);
printf("Наибольшее случайное число: %d.\n",
biggest);
printf("Наименьшее случайное число: %d.\n",
smallest);
printf("Среднее из случайных чисел: %.2f.\n",
average);
// При использовании malloc, не забудьте
// использовать free
free(randomNums);
return(0);
}
Не могу понять, я же должен освободить память во всех ячейках? Почему в строчке free(randomNums) я освобождаю только нулевую ячейку памяти? Если бы не книга, то с помощью цикла я бы прошёлся по каждой ячейке и очистил ее. RandonNums же указатель на массив этих ячеек памяти, или я что-то напутал?
Вы освобождаете не только ту ячейку памяти, на которую указывает указатель. К этой ячейке прицеплен сзади нее еще служебный блок памяти со структурой, в которой содержится разная информация, в том числе и о размере выделенного программе блока памяти. Функция malloc служит не только для нахождения свободного блока памяти заданного размера, но и для заполнения служебного блока памяти перед этим блоком, а возвращает указатель на начало только той части выделенной памяти, которая служит для размещения данных. Соответственно функция free находит по данном ей адресу не только начало памяти для данных, но и размещенный перед ней блок служебной информации, в котором указано, сколько памяти нужно освободить. При этом, естественно, освобождается не только память для данных, начинающаяся с указанного адреса, но и память, в которой перед этим адресом был размещен служебный блок информации.
И не считайте, что речь тут идет о реальной памяти - речь идет о виртуальной. В современных процессорах не зря миллиарды транзисторов, на самом деле всё гораздо сложнее, чем описывается в обычных пособиях, но ведь и обычному программисту всё это видеть, да и вообще знать об этом, и не надо. Это всё, так сказать, упрятано под ковер, на котором танцует пишущий программы программист.
Андрей Галлер
Андрей Галлер
62 928
Лучший ответ
Михаил Топоров Вау, огромное спасибо за такой развернутый ответ, теперь все встало на свои места:)
А выделял ты тоже по одной ячейке? Если нет, то почему тебе пришла в голову идея освобождать по одной?
Дело в менеджере кучи. Он хранит не только адрес, но и размер занятой памяти по этому адресу. Поэтому освобождать ее можно "махом", даже не указывая размеров.

>Если бы не книга, то с помощью цикла я бы прошёлся по каждой ячейке и очистил ее.
Интересно, как бы тебе это удалось, если у тебя только ОДИН указатель...

> RandonNums же указатель на массив этих ячеек памяти, или я что-то напутал?
Ничего не напутал. Указатель на массив - это и есть указатель на его нулевой элемент.
По
Подджьоавол
63 653
Михаил Топоров Так если он указывает на на нулевой элемент, то и рассуждая логически функция free должна освободить память именно нулевой ячейки массива выделенной динамической памяти
Михаил Топоров Вот иллюстрация, указатель же хранит нулевой элемент
Память выделяется не под каждый элемент в отдельности, а под весь массив сразу, одним цельным блоком. Поэтому и освобождается массив тоже весь целиком.
Менеджер памяти хранит список всех выделенных блоков, в котором записаны их адреса и размеры. Когда ты освобождаешь память он ищет в этом списке соответствующий адрес, смотрит сколько байтов выделялось и освобождает их все разом. Поэтому передаваемый функции free указатель должен быть тем же самым, который был возвращён функцией malloc. Если попытаться передать адрес какого-то произвольного элемента, то менеджер не найдёт нужный блок (потому что такого адреса нет в списке) и операция завершится ошибкой.
Боря Жаксимов
Боря Жаксимов
24 295