Каково поведение malloc, если мы не можем освободить его - PullRequest
1 голос
/ 15 февраля 2011

У меня вопрос по поводу поведения malloc.Существует 2 файла c

myTools.c и mainFile.c

mainFile.c is =>

int main(){
    int i=1;
    char *request="blablabla"//vary in situation.Not static
    while(i==1)//forever Loop
    {
      ...
      strcpy(response,getFile(request));
      ...
    }
}

myTools.c is ==>

.
.//something else
.
char *getFile(char *request)
{
  char *retVal;
  ...//some tcp request
  retVal=malloc(strlen(buffer));
  strcpy(retVal,buffer);
  ..//some char array operations
  return retVal;    
}
.
.//something else
.

Не могу найти способ освободить retVal или мне интересно, что мне нужно освободить retVal?

Он работает на uClinux на встроенной платформе m68k, поэтому память ограничена.

Может ли это вызвать проблемы с памятью или голодание?

Или повлияет на поведение программы во время выполнения?

Ответы [ 8 ]

4 голосов
/ 15 февраля 2011

Вы будете терять память каждый раз, когда вызывается getFile ().

Одним из решений будет:

while(i==1)//forever Loop
{
  ...
  char* temp = getFile( request );

  strcpy(response,temp);

  free( temp );
  ...
}
2 голосов
/ 15 февраля 2011

Поскольку вы возвращаете указатель из getFile(), возможно, вы могли бы просто присвоить его переменной указателя в main(), минуя двойное использование strcpy()?Тогда вы можете free() память в main().

Я не уверен, но не понимаю, почему это не сработает.

1 голос
/ 15 февраля 2011

Всякий раз, когда вы создаете интерфейс для какой-либо функциональности, вам нужно думать об управлении ресурсами - каково время жизни различных объектов и как управляется время жизни этих объектов.Это особенно верно для языков, таких как C и C ++, которые не реализуют сборку мусора, но даже в языках с сборкой мусора, необходимо подумать об этом (для ресурсов, не связанных с памятью, и, возможно, чтобы гарантировать, что ссылки на объекты не хранятсянеопределенно).

В любом случае, для C API могут иметь дело с «выходными» объектами несколькими способами.Вот некоторые из наиболее распространенных шаблонов:

  1. позволяет вызывающей стороне предоставить буфер или объект для размещения результатов в
  2. , чтобы вызываемый объект выделял буфер или объект и возвращал указатель навыделенный объект
  3. заставляет вызывающую функцию возвращать объект непосредственно из функции - это работает только для элементов, имеющих фиксированный размер (внутренний тип или структуру) - это не работает для произвольных строк.Поэтому я не буду обсуждать это дальше.

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

int getFile(char const* request, char* result, int result_size);

Вызывающая сторона передает указатель для размещения результатов и размера передаваемого в буфер.Функция может вернуть отрицательное число при сбое (скажем, сбой в сети) и размер результата при успехе.Если размер, возвращаемый в случае успеха, больше, чем предоставленный буфер, вызывающая сторона знает, что полный результат не был помещен в буфер (поскольку он недостаточно велик).Если вы идете по этому пути, важно принять во внимание символ-терминатор '\ 0' и четко указать, включен ли он в возвращаемое значение и завершается ли буфер, если результат слишком велик (что я рекомендую).Кроме того, убедитесь, что при передаче буфера нулевого размера функция ничего не записывает в буфер независимо от того, передан ли указатель, отличный от NULL (это обычный шаблон, когда функция возвращает требуемый размербуфер в этом случае).

Для варианта 2 функция будет выделять соответствующий буфер и возвращать указатель на него вызывающей стороне.В этом случае у вызывающей стороны должен быть способ освободить буфер, когда это сделано, чтобы избежать утечки памяти.Одним из способов является документирование того, что вызывающая сторона должна вызвать free() для указателя, возвращаемого функцией (подразумевая, что функция будет назначать указатель, используя malloc() или calloc()).Другой - чтобы интерфейс для использования функции также включал подпрограмму освобождения, возможно, getFile_free() (я бы определенно переосмыслил названия этих функций, если бы я пошел по этому пути).Это дает реализации этого API свободу распределять буферы, которые он возвращает, как считает нужным - он не ограничен использованием malloc() / free() (хотя это возможно).

Для встроенной системы (особеннонебольшие (возможно, не системы на основе Linux), я думаю, что люди могут выбрать вариант 1. Это дает пользователю API свободу вообще избегать динамического выделения памяти, и для встроенных систем характерно не использовать динамическую память.Кроме того, система, использующая динамическое выделение памяти, может по-прежнему использовать шаблон варианта 1 - она ​​просто требует немного больше работы, но эта работа может быть обернута во что-то, что в точности похоже на вариант 2, так что вы можете получить свой торт и съесть его тоже.

1 голос
/ 15 февраля 2011

Во встроенных системах malloc () часто используется для выделения памяти при запуске, которая будет использоваться во время работы системы и никогда не освобождаться () d.

Но вы не должны делать что-то подобное периодически, так как оно израсходует вашу память, если вы не освободите () это снова.

То, что вы делаете в своем примере - это утечка памяти на один буфер каждый раунд в цикле while.

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

Если вы заранее знаете, сколько памяти вам понадобится заранее: выделите буфер один раз за пределами цикла и повторно используйте его в цикле:

response = malloc(MAX_BUF_SIZE);
while (1) {
    get_file(response, MAX_BUF_SIZE);    /* always good to pass the buf size and check */      ... use response ...
}

Или, если вы заранее не знаете размер, один шаблон может быть:

response = NULL;
size = 0;
while (1) {
    get_file(&response, &size);
    ... use response ...
}

void get_file(char **buf, int *s)
{
    size = ... somehow determine the needed size ...
    if (size > *s)
        *buf = realloc(*buf, size);     /* only does free/malloc internaly if it has to no room */
    strncpy(*buf, whatver, size);
 }

И вы всегда должны использовать strncpy и никогда strcpy, пожалуйста!

Использование strcpy в вашем примере сбивает с толку, не видно, где отклик получает свою память, он статически распределен? Это было раньше выслано?

Если ответ уже указывает на пустой буфер, вы должны просто передать указатель на буфер в getFile() и напрямую скопировать туда buffer.

Если ответ не указывает на допустимый буфер памяти, он все равно не будет работать.

1 голос
/ 15 февраля 2011
strcpy(response,getFile(request)); 

Следует разделить на:

char *tmp = getFile(request);
strcpy(response, tmp );
free( tmp );

Иногда это называется распределением стиля strdup (), что не рекомендуется. Вы должны выделить буфер в вызывающем коде, например:

char *buf2 = malloc(strlen(buffer));   

и звоните

getFile( buf2, strlen( buffer ), input );
// use buf2
free( buf2 );
1 голос
/ 15 февраля 2011

strcpy(response,getFile(request)) может привести к сбою вашей программы, поскольку response указывает на постоянную память.

0 голосов
/ 15 февраля 2011

Во-первых, * retVal помещается в Функция getFile, таким образом, область действия * retVal ограничен этой конкретной функцией и объемом используемой памяти будет автоматически установлен на БЕСПЛАТНО один раз функция завершается / возвращается.

Область действия переменной определяет ее время жизни / удобство использования, также как только время жизни истекло, и вы не освобождаете блок, в котором остаются данные, доступ к нему можно получить через указатель, однако блок памяти помечен как БЕСПЛАТНЫЙ. и впоследствии перезаписывается без каких-либо проблем.

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

Надеюсь, это поможет, если нет, я буду рад помочь, просто дайте мне знать:)

0 голосов
/ 15 февраля 2011

Да, это в конечном итоге приведет к нехватке памяти, что приведет к сбою вашей программы.

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

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

...