Почему нет утечки памяти? - PullRequest
4 голосов
/ 19 июня 2010

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

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

Я создал простой тест, который вызывает эту функцию в цикле, каждый раз с различными длинами cString и параметром nMaxChars. Потом я посмотрел статус ВМ. Выделение памяти для моей тестовой программы и свободной памяти никогда не менялось.

Мне кажется, он должен увеличиваться каждый раз, когда вызывается malloc, а не бесплатно.

static char *ToHexString(const char *cString,int nMaxChars)
{
    static char *cStr;



    /*if (80>strlen(cString))
        nRawChars=strlen(cString);
    if (nMaxChars>nRawChars)
        nRawChars=nMaxChars;
    */
    if (nMaxChars==0)
        nMaxChars=80;

    printf("There are %i chars\n",nMaxChars);

    char *cStr1;
    char *cStr2;
    char *cStr3;
    int nLen=nMaxChars*6;
    cStr=calloc(nLen,sizeof(char));

    cStr1=calloc(10,sizeof(char));
    cStr2=calloc(nLen,sizeof(char));
    cStr3=calloc(nLen,sizeof(char));
    cStr1[0]='\0';
    cStr2[0]='\0';
    cStr3[0]='\0';
    int nC1=0;
    int nRowCnt=0;

    for (nC1=0;nC1<nMaxChars;nC1++)
    {
        ++nRowCnt;
        if (cString[nC1]==0x00)
            snprintf(cStr1,8,"[00] ");
        else
            snprintf(cStr1,8,"[%02x] ",(unsigned char)cString[nC1]);

        if ( (nRowCnt%8==0) )
        {
            snprintf(cStr3,nLen,"%s%s\n",cStr2,cStr1);
        }
        else
            snprintf(cStr3,nLen,"%s%s",cStr2,cStr1);

        snprintf(cStr2,nLen,"%s",cStr3);
    }
    snprintf(cStr,nLen,"%s",cStr3);
    free(cStr1);
    free(cStr2);
    free(cStr3);
    return(cStr);
}

Вот процедура вызова:

for (i=0;i<100;i++)
{
    memset(&cBuff, 0,255);
    printf("Reading %s now..\n",cPort);
    while (sleep(1)==-1);
    nChars=read(nPort, cBuff, 255);
    //printf("Read %i chars from %s\n",nChars,cPort);
    if (nChars<=0)
        printf("Read 0 chars from %s\n",cPort);
    else
        printf("Read %i chars from %s\n%s\n",nChars,cPort,ToHexString(cBuff,nChars));
}

Ответы [ 4 ]

10 голосов
/ 19 июня 2010

Следующая утечка:

static void memeat(void)
{
        static char *foo = NULL;

        foo = malloc(1024);

        return;

}

Valgrind выход:

==16167== LEAK SUMMARY:
==16167==    definitely lost: 4,096 bytes in 4 blocks
==16167==    indirectly lost: 0 bytes in 0 blocks
==16167==      possibly lost: 0 bytes in 0 blocks
==16167==    still reachable: 1,024 bytes in 1 blocks
==16167==         suppressed: 0 bytes in 0 blocks
==16167== Rerun with --leak-check=full to see details of leaked memory

Примечание. still reachable (1024 байта) - это результат последнего ввода memeat(). Статический указатель все еще содержал действительную ссылку на последний блок memeat(), выделенный при выходе из программы. Только не предыдущие блоки.

Следующее НЕ утечка:

static void memeat(void)
{
        static char *foo = NULL;

        foo = realloc(foo, 1024);

        return;

}

Выход Valgrind:

==16244== LEAK SUMMARY:
==16244==    definitely lost: 0 bytes in 0 blocks
==16244==    indirectly lost: 0 bytes in 0 blocks
==16244==      possibly lost: 0 bytes in 0 blocks
==16244==    still reachable: 1,024 bytes in 1 blocks
==16244==         suppressed: 0 bytes in 0 blocks
==16244== Rerun with --leak-check=full to see details of leaked memory

Здесь адрес foo, на который указывает указатель, был освобожден, а foo теперь указывает на вновь выделенный адрес и будет продолжать делать это при следующем вводе memeat().

Пояснение:

Тип хранения static говорит о том, что указатель foo будет указывать на тот же адрес, что и инициализированный, каждый раз при входе в функцию. Однако, если вы меняете этот адрес каждый раз, когда функция вводится с помощью malloc() или calloc(), вы теряете ссылку на блоки из предыдущего распределения. Отсюда и утечка, поскольку любой из них собирается вернуть новый адрес.

«Все еще достижимо» в valgrind означает, что все выделенные блоки кучи все еще имеют действительный указатель для доступа / манипулирования / освобождения их при выходе. Это похоже на выделение памяти в main() и не освобождение ее, а просто использование ОС для восстановления памяти.

Короче говоря, да - у вас есть утечка. Тем не менее, вы можете исправить это довольно легко. Просто отметьте, что вы действительно полагаетесь на свою ОС для восстановления памяти, если только вы не добавите в свою функцию еще один аргумент, который просто скажет ToHexString вызвать free для статического указателя, который вы могли бы использовать при выходе.

Аналогично этому: (полная программа тестирования)

#include <stdlib.h>

static void memeat(unsigned int dofree)
{
        static char *foo = NULL;

        if (dofree == 1 && foo != NULL) {
                free(foo);
                return;
        }

        foo = realloc(foo, 1024);

        return;

}


int main(void)
{
        unsigned int i;

        for (i = 0; i < 5; i ++)
                memeat(0);

        memeat(1);
        return 0;
}

Выход Valgrind:

==16285== HEAP SUMMARY:
==16285==     in use at exit: 0 bytes in 0 blocks
==16285==   total heap usage: 6 allocs, 6 frees, 6,144 bytes allocated
==16285==
==16285== All heap blocks were freed -- no leaks are possible

Примечание к окончательному выводу :

Да, 6144 байта фактически были выделены в соответствии с тем, что malloc() вернулось во время выполнения программы, но это просто означает, что статический указатель был освобожден, а затем перераспределен в соответствии с количеством введенных memeat(). Фактическое использование кучи программы в любой момент времени было фактически всего 2 * 1024, 1k для выделения нового указателя, в то время как старый еще существовал, ожидая копирования в новый.

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

1 голос
/ 19 июня 2010

Я хотел бы отметить две вещи, которые приходят мне в голову при проверке кода:

cStr=calloc(nLen,sizeof(char));

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

Кроме того, поскольку у вас cStr объявлен как статический указатель на char *, вы вообще не освобождаете его, фактически эта строка доказывает это:

printf("Read %i chars from %s\n%s\n",nChars,cPort,ToHexString(cBuff,nChars));
                                                  ^^^^^^^^^^^

Вам лучше сделать это так:

char *hexedString;
hexedString = ToHexString(cBuff, nChars);
....
printf("Read %i chars from %s\n%s\n",nChars,cPort,hexedString);
....
free(hexedString);
1 голос
/ 19 июня 2010

Это утечка памяти. Если вы постоянно вызываете функцию, память, используемая программой, увеличивается. Например:

int main() {
   while (1) {
      ToHexString("testing the function", 20);
   }
}

Если вы запустите это и увидите процесс с помощью инструмента системного мониторинга, вы увидите, что используемая память постоянно увеличивается.

Утечка, вероятно, не очевидна в вашей программе, потому что функция пропускает только несколько байтов каждый вызов и вызывается не очень часто. Так что увеличение использования памяти не очень заметно.

0 голосов
/ 19 июня 2010

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

...