2013-12-20 5 views
6

Разделяет ли strdup другую зону памяти и каждый раз создает другой указатель?strdup и утечка памяти

Например: имеет ли следующий код ошибку утечки памяти?

void x(char** d, char* s){ 
    *d = strdup(s); 
} 

int main(){ 
    char* test = NULL; 
    x(&test, "abcd"); 
    x(&test, "etc"); 
    return 0; 
} 
+3

Да, это утечка памяти. –

+2

Как он мог работать, если он не выделял больше памяти? Куда бы он поместил все дубликаты? – Barmar

+1

@Barmar Программа не принимает входных данных и не производит никаких выходов, поэтому, как она могла работать без выделения памяти, это может быть как «int main() {return 0; } '. Конкуренту разрешено делать эту оптимизацию, даже. – Kaz

ответ

6

Да, программа утечек памяти, так как он выделяет объекты, а затем теряет ссылки на них.

В первый раз это происходит в строке:

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.

+0

Спасибо за отличный ответ, я многому научился! :) Я понимаю, что это лучшая реализация, и я понимаю, почему, но если мне пришлось вызвать функцию как strealloc (& test, «etc»), как мне ее реализовать в этом случае? Я предполагаю, что я должен создать новый указатель внутри функции, выделить для него память и после копирования строки, если все пойдет хорошо, только тогда я (таким образом, не повлияю на origptr каким-либо образом, если распределение памяти пошло не так) что-то вроде * origptr = newptr (и до этого бесплатного (* origptr). Пожалуйста, дайте мне знать, если это имеет смысл. – Zack

+1

Использовать '& test' и обновлять указатель на месте или возвращать новое значение - это просто Подробное описание интерфейса. Если возникает ошибка выделения памяти, вам необходимо знать: 2) возможно, не хотите потерять старый указатель. Таким образом, интерфейс с возвратом значений лучше в этом отношении.Это не меняет реализацию; мы все равно можем использовать 'realloc', если функция вызывается как' strealloc (& test, "etc") '. В этом случае неплохо было бы также вернуть значение 'int':' 1', если оно работает, '0', если оно не выполнено. – Kaz

+0

Является ли это хорошей реализацией в этом случае? void * strealloc (char ** origptr, char * strdata) { size_t nbytes = strlen (strdata) + 1; char * newptr = malloc (nbytes); if (newptr! = NULL) { memcpy (newptr, strdata, nbytes); бесплатно (* origptr); * origptr = newptr; } return newptr; } – Zack

1

Да, он выделяет память и утечки, если вы ее не освободите. Из man page:

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

new_s = strdup(s) по существу эквивалентно:

new_s = malloc(strlen(s)+1); 
strcpy(new_s, s); 
1

Рассмотрим следующее определение strdup:

#include <string.h> 
char *strdup(const char *string); 

strdup оставляет пространство для хранения копии string по телефону malloc. Ожидается, что аргумент строки этой функции должен содержать символ null (\0), обозначающий конец строки. Не забудьте освободить хранилище, зарезервированное с вызовом strdup.

Вы должны free шнур самостоятельно.

Смежные вопросы