2014-01-08 3 views
6

Мне до сих пор не приходилось использовать strdup(stringp) вместе с strsep(&stringp_copy, token), и я думаю, что это вызывало утечку памяти.strdup() утечка памяти даже после освобождения()

(strdup() всегда free «d нормально раньше.)

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

Оригинальный код (кратко):

const char *message = "From: username\nMessage: basic message\n"; 
char *message_copy, *line, *field_name; 
int colon_position; 
message_copy = strdup(message); 

while(line = strsep(&message_copy, "\n")) { 
    printf(line); 
    char *colon = strchr(line, ':'); 
    if (colon != NULL) { 
    colon_position = colon - line; 
    strncpy(field_name, line, colon_position); 
    printf("%s\n", field_name); 
    } 
} 

free(message_copy); 

Новый код, который не просачивается:

const char *message = "From: username\nMessage: basic message\n"; 
char *message_copy, *freeable_message_copy, *line, *field_name; 
int colon_position; 
freeable_message_copy = message_copy = strdup(message); 

while(line = strsep(&message_copy, "\n")) { 
    printf(line); 
    char *colon = strchr(line, ':'); 
    if (colon != NULL) { 
    colon_position = colon - line; 
    strncpy(field_name, line, colon_position); 
    printf("%s\n", field_name); 
    } 
} 

free(freeable_message_copy); 

Как это message_copy указатель переписывается в первом коде? или это?

+2

Комментарий: не следует 'printf (line)'; если 'line' содержит то, что выглядит как' printf() 'форматирование строки преобразования спецификации, он в конечном итоге чтения или записи неправильной памяти. Используйте либо «fputs (line, stdout)», либо «printf» («% s», строка) »- либо безопасны, либо эквивалентны. –

+1

'message_copy' перезаписывается strsep. Прочтите этого человека. – Marian

+0

Спасибо, @JonathanLeffler. 'printf (строка)' был для быстрой и грязной отладки. – oliverseal

ответ

9

Функция strsep() принимает указатель на исходную строку (message_copy) и изменяет его вернуть новый указатель на «следующий» лексемы

const char *message = "From: username\nMessage: basic message\n"; 
char *message_copy, *original_copy; 
//here you have allocated new memory, a duplicate of message 
message_copy = original_copy = strdup(message); 

Распечатайте указатель здесь,

printf("copy %p, original %p\n", message_copy, original_copy); 

Обратите внимание, что, как вы используете strsep(), вы изменяете message_copy,

char* token; 
//here you modify message_copy 
while(token = strsep(&message_copy, "\n")) { 
    printf("%s\n", token); 
} 

Th это иллюстрирует измененную message_copy, а original_copy неизменна,

printf("copy %p, original %p\n", message_copy, original_copy); 

Поскольку message_copy не указывает на исходное strdup() результат это было бы неправильно,

free(message_copy); 

Но держать вокруг оригинального strdup() результат, и это бесплатно работает

//original_copy points to the results of strdup 
free(original_copy); 
+0

Я полностью пропустил, что он изменил указатель на странице 'strsep' man. – oliverseal

5

Поскольку strsep() изменяет аргумент message_copy, вы пытались освободить указатель, который не был возвращен malloc() и др. Это вызвало бы жалобы от некоторых библиотек malloc() и от valgrind. Это также неопределенное поведение, обычно приводящее к сбоям в коротком порядке (но сбой в коде в неудобном месте, не связанном с кодом, который нанес ущерб).

Фактически, ваша петля выполняет итерацию до тех пор, пока значение message_copy не будет установлено в NULL, поэтому вы освобождаете NULL, который определяется и безопасен, но он также не работает. Он не освободил указатель, выделенный через strdup().

Резюме:

  • Только свободные указатели, возвращаемые распределители памяти.
  • Не допускайте свободных указателей в середине или конце блока, возвращаемых распределителями памяти.
1

Прочитать статью strsep man here.

Короче функция strsep будет изменить исходный указатель на символ, который передается в функцию, заменяя каждый occurrance из разделителя с \0, и оригинальным указатель на символе затем обновляется, чтобы указать мимо \0.

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

0

на странице человека,

... Этот токен завершается перезаписью разделителя нулевым байтом ('\ 0') и *stringp is updated to point past the token ....

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