2013-05-27 6 views
2

Я пытаюсь построить функцию str_replace в C (чтобы узнать C). Для того, чтобы сделать вещи немного проще, я решил создать две вспомогательные функции, одна из которых имеет следующий прототип:Утечка памяти, работающая со строками C

char * str_shift_right(const char * string, char fill, int32_t n);

Он принимает строку и добавляет символ fill на n-й позиции в заданной строкой. Вот полный код:

// replace the nth char with 'fill' in 'string', 0-indexed 
char * str_shift_right(const char * string, char fill, int32_t n) { 
    // +1 the null byte, +1 for the new char 
    int32_t new_size = (int32_t) strlen(string) + 2; 
    char * new_string = NULL; 
    new_string = calloc(new_size, sizeof(char)); 

    new_string[new_size - 1] = '\0'; 
    int32_t i = 0; 
    while (i < strlen(string) + 1) { 
     // insert replacement char if on the right position 
     if (i == n) { 
      new_string[i] = fill; 

     // if the replacement has been done, shift remaining chars to the right 
     } else if (i > n) { 
      new_string[i] = string[i - 1]; 

     // this is the begining of the new string, same as the old one 
     } else { 
      new_string[i] = string[i]; 
     } 

     i++; 
    } 

    return new_string; 
} 

Я хотел убедиться, эта функция не была утечка памяти, поэтому я попытался выполнив следующий код:

int main(int argc, const char * argv[]) 
{  
    do { 
     char * new_str = str_shift_right("Hello world !", 'x', 4); 
     printf("%s", new_str); 
     free(new_str); 
    } while (1); 

    return 0; 
} 

Однако при просмотре использования памяти с монитором активности (приложение Mac OSX для тех, кто не знаком, вроде диспетчера процессов в Windows), похоже, что RAM быстро съедается, и он не становится доступным, когда программа перестает выполняться.

Это утечка памяти? Если да, то что я сделал не так? Разве не вызов free(new_str) освобождает память?

Благодарим за помощь.

Редактировать 1: Исправлено одной ошибкой, обнаруженной PaulR. Проблема остается.

+1

Почему 'calloc', когда вы сразу написать по всем нулям так красиво инициализируются для вас? –

+4

'new_string [new_size] = '\ 0';' является недопустимой записью - вы, вероятно, имели в виду 'new_string [new_size - 1] = '\ 0';'? –

+1

Вы также можете использовать инструмент, например 'valgrind', чтобы проверить вашу программу на утечку памяти. – tomato

ответ

6

Кажется, что RAM быстро съедается и не становится доступным, когда программа прекращает выполнение.

Какое использование оперативной памяти вы используете? Общее использование ОЗУ в системе?

Если да, то, что вы видите, вероятно, является памятью, которую использует ваш терминал - каждый символ, который выдает ваша программа, будет храниться в ОЗУ с помощью терминала (хотя он, вероятно, начнет бросать материал с определенным пределом). Попробуйте еще раз, но на этот раз, предотвратить выход из не появляется в терминале:

./program > /dev/null 

По общему правилу, независимо от того, сколько памяти вы протечки, она всегда будет освобождена автоматически, когда программа прекращается. И я не могу обнаружить никаких утечек в вашей программе.

2

free не должен освободить память системы (например, с использованием munmap(2), потому что malloc обычно предпочитают повторно использовать ранее free -d память вместо его приобретения из ядра (например, с помощью mmap)

Интуиция является то, что системные вызовы, управляющие адресным пространством памяти (т.е. mmap & munmap) стоит довольно дорого, поэтому большинство malloc & free реализации пытается повторно использовать память внутри, когда это возможно.

Если вы подозреваете утечку памяти, попробуйте valgrind (последние версии были перенесены в MacOSX). Конечно компилировать код с отладочной информацией и все предупреждения (например, gcc -Wall -g или, возможно, clang -Wall -g)

+1

Он должен повторно использовать выделенное пространство. Поэтому, если он «быстро поедает память», это повторное использование не работает должным образом. –

+1

Это правильно, но я думаю, что это не имеет никакого отношения к тому, что испытывает OP. – thejh

2

Update: как вы измерения? Это не должно быть возможным в UNIX: «[память] не становится доступной, когда программа прекращает выполнение». Может быть, вы просто смотрите на неправильный номер?!?

Я не вижу, где должна быть память. Попробуйте использовать такие инструменты, как valgrind!

Вышеупомянутая недействительная запись обсуждалась несколько раз. Возможно, вы уничтожаете информацию управления памятью с этим. Я видел, что библиотеки C используют байты только infront и/или после каждого выделенного фрагмента памяти для отслеживания выделенных фрагментов памяти. Так что, возможно, вы обнуляете размер таким образом и управляете памятью таким образом. Так что действительно, использовать valgrind и аналогичные инструменты.

Однако вы также можете упростить свой код.

  1. Не нужно обнулять память, которую вы собираетесь полностью перезаписать.
  2. Не нужно использовать петлю, когда вы уже знаете позиции.
  3. Вы также можете скопировать трейлинг-код \0.
  4. Фактически dosize_t, что неподписанный. Ваш код выше не будет предупреждать, если fill отрицательный. С size_t он не должен быть отрицательным.

Таким образом, это должно быть достаточно (я не компилировать тест это - вы можете):

char* str_shift_right(const char* string, char fill, size_t n) { 
    size_t len = strlen(string); 
    char* new_string = malloc(len + 2, sizeof(char)); 
    memcpy(new_string, string, n); 
    new_string[n] = fill; 
    memcpy(new_string + n + 1, string + n, len - n + 1); 
    return new_string; 
} 

Последние memcpy также скопирует заднюю \0 здесь. В целом, компилятор C может оптимизировать этот код лучше, memcpy обычно достаточно хорошо позаботился. Кроме того, на самом деле это намного проще читать.

0

Хотя было сказано, я думаю, что стоит подчеркнуть, что, если вы подозреваете, что есть утечка памяти в вашей программе, не делать выводы только системные часы мониторами, но с использованием Valgrind вместо этого.

Скачать отсюда:

http://valgrind.org/downloads/current.html

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