Да, программа утечек памяти, так как он выделяет объекты, а затем теряет ссылки на них.
В первый раз это происходит в строке:
x(&test, "etc");
Переменная test
держит одну и единственную копию указателя, который был выделен в предыдущем вызове x
. Новый вызов x
перезаписывает этот указатель. В этот момент указатель течет.
Это то, что означает утечку памяти: потерять все ссылки на существующий динамически выделенный кусок хранилища.
Вторая утечка возникает, когда возвращается функция main
. В этот момент переменная test
уничтожается, и эта переменная содержит единственную копию указателя на дубликат строки "etc"
.
Иногда в программах на C иногда нам не нужны утечки этого второго типа: память, которая не освобождается при завершении программы, но которая не выделяется снова и снова в цикле (так что это не вызывает проблема роста памяти).
Если программа когда-либо интегрирована в другую программу (например, как разделяемую библиотеку), где исходная функция main
становится функцией запуска, которая может быть вызвана повторно в одной и той же программной среде, обе утечки превратятся в проблемы.
Функция POSIX strdup
ведет себя так же, как это:
char *strdup(const char *orig)
{
size_t bytes = strlen(orig) + 1;
char *copy = malloc(bytes);
if (copy != 0)
memcpy(copy, orig, bytes);
return copy;
}
Да; он каждый раз выделяет новое хранилище.
Если у вас на вашем изображении C есть сборщик мусора (например, Boehm), то возможно, что просочившееся хранилище переработано, и поэтому strdup
может повторно использовать одну и ту же память для второго выделения. (Тем не менее, сборщик мусора не собирается пинать после всего одного распределения, если только он не работает в режиме стресс-теста для очистки ошибок.)
Теперь, если вы действительно хотите повторно использовать память с перераспределить, то вы можете изменить свою x
функцию вдоль этих линий:
#include <stdlib.h>
#include <string.h>
void *strealloc(char *origptr, char *strdata)
{
size_t nbytes = strlen(strdata) + 1;
char *newptr = (char *) realloc(origptr, nbytes); /* cast needed for C++ */
if (newptr)
memcpy(newptr, strdata, nbytes);
return newptr;
}
(Кстати, внешние имена, начинающиеся с str
находятся в ISO C зарезервированное пространство имен, но strealloc
слишком красивое имя для отказа.)
Обратите внимание, что интерфейс отличается. Мы не передаем указатель на указатель, а вместо него представляем интерфейс с размером realloc
. Вызывающий может проверить возвращаемое значение для NULL, чтобы обнаружить ошибку распределения, не указав в этом случае неудобно перезаписанный указатель.
main
функция теперь выглядит следующим образом:
int main(void)
{
char *test = strealloc(NULL, "abcd");
test = strealloc(test, "etc");
free(test);
return 0;
}
, как и прежде, нет проверки ошибок. Если первые strealloc
были сбой, test
тогда является нулевым. Это не так, поскольку все равно перезаписывается, и первый аргумент strealloc
может быть нулевым.
Для подключения утечки памяти требуется только один free
.
Да, это утечка памяти. –
Как он мог работать, если он не выделял больше памяти? Куда бы он поместил все дубликаты? – Barmar
@Barmar Программа не принимает входных данных и не производит никаких выходов, поэтому, как она могла работать без выделения памяти, это может быть как «int main() {return 0; } '. Конкуренту разрешено делать эту оптимизацию, даже. – Kaz