Вопрос об утечке памяти в C после движущегося указателя (Что точно освобождено?)

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

(defun duplicate-line()
  (interactive)
  (move-beginning-of-line 1)
  (kill-line)
  (yank)
  (open-line 1)
  (next-line 1)
  (yank)
)
(global-set-key (kbd "C-d") 'duplicate-line)
9
задан Sinan Ünür 12 August 2009 в 15:48
поделиться

11 ответов

Вы не можете передать указатель, который не был получен из malloc , calloc или realloc в free (кроме NULL ).

Вопрос 7.19 в C FAQ имеет отношение к вашему вопросу.

Объясняются последствия вызова неопределенного поведения ] здесь .

18
ответ дан 4 December 2019 в 06:22
поделиться

Это неопределенное поведение в стандарте, так что вы можете ' Я ни на что не полагаюсь.

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

Последнее, что я посмотрел, было два основных метода реализации.

Один из них - сохранить отдельная запись выделенных блоков вместе с выделенным адресом. Функция free () просматривает блок, чтобы узнать, что освободить. В этом случае он, скорее всего, его просто не найдет, а может просто ничего не делать. Утечка памяти. Однако нет никаких гарантий.

Один из них - хранить информацию о блоке в части памяти непосредственно перед адресом распределения. В этом случае free () использует часть блока в качестве дескриптора блока, и в зависимости от того, что ' s хранится там (что может быть что угодно), это что-то освободит. Это может быть слишком маленькая или слишком большая область. Очень вероятно повреждение кучи.

Итак, я бы ожидал либо утечки памяти (ничего не освобождается), либо повреждения кучи (слишком много помечается как свободная, а затем перераспределяется).

7
ответ дан 4 December 2019 в 06:22
поделиться

Да, это неопределенное поведение. По сути, вы освобождаете указатель, который вы не использовали malloc .

5
ответ дан 4 December 2019 в 06:22
поделиться

Вы не можете передать указатель, который вы не получили из malloc (или calloc или realloc ...) в бесплатно . Это включает смещения в блоки, которые вы получили из malloc . Нарушение этого правила может привести к чему угодно . Обычно это заканчивается наихудшей из возможных возможностей в самый неподходящий момент.

В качестве дальнейшего примечания, если вы хотите усечь блок, есть законный способ сделать это:

#include <stdio.h>
#include <string.h>

int main() {
    char *new_s;
    char *s = malloc(1024);
    strcpy(s, "Some string");

    new_s = realloc(s, 5);
    if (!new_s) {
        printf("Out of memory! How did this happen when we were freeing memory? What a cruel world!\n");
        abort();
    }
    s = new_s;

    s[4] = 0; // put the null terminator back on
    printf("%s\n", s); // prints Some

    free(s);
    return 0;
}

realloc работает как для увеличения и сжимать блоки памяти, но может (или не может) перемещать память для этого.

4
ответ дан 4 December 2019 в 06:22
поделиться

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

Большинство реализаций malloc выделяют небольшую (обычно 4 байта) область заголовка непосредственно перед возвратом ptr. Это означает, что когда вы выделили 1024 байта, malloc фактически зарезервировал 1028 байтов. Когда вызывается free (ptr), если ptr не равно 0, он проверяет данные в ptr - sizeof (заголовок). Некоторые распределители реализуют проверку работоспособности, чтобы убедиться, что это допустимый заголовок, который может обнаружить неправильный ptr и подтвердить или завершить работу. Если проверка на работоспособность отсутствует или она проходит ошибочно, свободная процедура будет работать с любыми данными, которые находятся в заголовке.

2
ответ дан 4 December 2019 в 06:22
поделиться

Что меня интересует: освобождены ли также 5 байтов, расположение которых в памяти следует сразу за концом исходных 1024 байтов, или их просто оставили в покое?

И то, и другое. Результат не определен, поэтому компилятор может делать любое из них или все, что ему действительно нужно. Конечно (как и во всех случаях «неопределенного поведения») для конкретной платформы и компилятора есть конкретный ответ, но любой код, который полагается на такое поведение, - плохая идея.

2
ответ дан 4 December 2019 в 06:22
поделиться

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

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

В черновике C99 (у меня нет под рукой последнего C99) есть что сказать по этой теме:

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

По моему опыту, двойное освобождение или освобождение "указателя", которое не было возвращено через malloc приведет к повреждению памяти и / или сбою, в зависимости от вашей реализации malloc . Сотрудники службы безопасности по обе стороны забора использовали это поведение не раз, чтобы делать разные интересные вещи , по крайней мере, в ранних версиях широко используемого пакета Doug Lea malloc .

]
1
ответ дан 4 December 2019 в 06:22
поделиться

Реализация библиотеки может поместить некоторую структуру данных перед указателем, который она вам возвращает. Затем в free () он уменьшает указатель, чтобы добраться до структуры данных, сообщая ей, как поместить память обратно в свободный пул. Таким образом, 5 байтов в начале строки Some интерпретируются как конец структуры struct , используемой алгоритмом malloc () . Возможно, конец 32-битного значения, например размер выделенной памяти, или ссылка в связанном списке. Это зависит от реализации. Какими бы ни были подробности, это просто приведет к сбою вашей программы. Как указывает Синан, если вам повезет!

0
ответ дан 4 December 2019 в 06:22
поделиться

Краткая версия: это неопределенное поведение.

Полная версия: я проверил сайт CWE и обнаружил, что, хотя это плохая идея, похоже, ни у кого нет твердого ответа. Вероятно, потому что он не определен.

Я предполагаю, что большинство реализаций, при условии, что они не завершаются сбоем , либо освободят 1019 байтов (в вашем примере), либо освободят 1024 и получат двойной или аналогичный на последних пяти. Пока просто говоря теоретически, это зависит от того, работает ли процедура malloc. Таблицы внутренней памяти содержат адрес и длину или начальный и конечный адрес.

В любом случае, это явно не лучшая идея. : -)

-2
ответ дан 4 December 2019 в 06:22
поделиться

Давайте поумней ... free () - это не черная дыра. По крайней мере, у вас есть исходный код CRT. Кроме того, вам понадобится исходный код ядра.

Конечно, поведение не определено, поскольку CRT / OS решает, что делать. Но это не мешает вам узнать, что на самом деле делает ваша платформа.

Быстрый взгляд на Windows CRT показывает, что free () ведет прямо к HeapFree () с использованием специфическая куча CRT. Помимо этого, вы попадаете в RtlHeapFree () , а затем в системное пространство (NTOSKRN.EXE) с менеджером памяти Mm * () .

Во всех этих пути кода. Но выполнение разных действий с памятью приведет к различным путям кода. Отсюда и истинное определение undefined.

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

Это возможный вариант в вашем случае освобождения памяти на несколько байтов в вашем блоке (или перезаписи выделенного вами размера ). Конечно, вы можете обмануть это и написать маркер конца блока самостоятельно в нужном месте. Это позволит вам пройти проверку CRT, но по мере продвижения кода будет происходить больше неопределенного поведения. Могут произойти три вещи: 1) абсолютно никакого вреда, 2) повреждение памяти в куче CRT, или 3) исключение, выброшенное любой из функций управления памятью.

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

Это возможный вариант в вашем случае освобождения памяти на несколько байтов в вашем блоке (или перезаписи выделенного вами размера ). Конечно, вы можете обмануть это и написать маркер конца блока самостоятельно в нужном месте. Это позволит вам пройти проверку CRT, но по мере продвижения кода будет происходить больше неопределенного поведения. Могут произойти три вещи: 1) абсолютно никакого вреда, 2) повреждение памяти в куче CRT, или 3) исключение, выброшенное любой из функций управления памятью.

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

Это возможный вариант в вашем случае освобождения памяти на несколько байтов в вашем блоке (или перезаписи выделенного вами размера ). Конечно, вы можете обмануть это и написать маркер конца блока самостоятельно в нужном месте. Это позволит вам пройти проверку CRT, но по мере продвижения кода будет происходить больше неопределенного поведения. Могут произойти три вещи: 1) абсолютно никакого вреда, 2) повреждение памяти в куче CRT, или 3) исключение, выброшенное любой из функций управления памятью.

Это возможно в вашем случае для освобождения памяти на несколько байтов в вашем блоке (или перезаписи выделенного вами размера). Конечно, вы можете обмануть это и написать маркер конца блока самостоятельно в нужном месте. Это позволит вам пройти проверку CRT, но по мере продвижения кода будет происходить больше неопределенного поведения. Могут произойти три вещи: 1) абсолютно никакого вреда, 2) повреждение памяти в куче CRT, или 3) исключение, выброшенное любой из функций управления памятью.

Это возможно в вашем случае для освобождения памяти на несколько байтов в вашем блоке (или перезаписи выделенного вами размера). Конечно, вы можете обмануть это и написать маркер конца блока самостоятельно в нужном месте. Это позволит вам пройти проверку CRT, но по мере продвижения кода будет происходить больше неопределенного поведения. Могут произойти три вещи: 1) абсолютно никакого вреда, 2) повреждение памяти в куче CRT, или 3) исключение, выброшенное любой из функций управления памятью.

0
ответ дан 4 December 2019 в 06:22
поделиться

Это делает не компилятор, а стандартная библиотека. Поведение не определено. Библиотека знает, что она предоставила вам исходные s . s + 5 не назначается ни одному блоку памяти, известному библиотеке, даже если он находится внутри известного блока. Так что это не сработает.

2
ответ дан 4 December 2019 в 06:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: