realloc и free вызывают двойное освобождение или коррупцию

Потерпите меня. Я не программировал на c 8 лет и совершенно сбит с толку, почему мои манипуляции со строками не работают. Я пишу программу, которая зацикливается вечно. В цикле я инициализирую два указателя char, каждый из которых передается функции, которая добавляет текст в указатель char (массив). Когда функции выполнены, я распечатываю указатель char и освобождаю два указателя char. Однако программа умирает после 7 итераций со следующим сообщением об ошибке

* обнаружен glibc * ./test: двойное освобождение или повреждение (fasttop): 0x0804a168 ***

#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include string.h
#include stdio.h
#include stdlib.h
#include errno.h
#include time.h

char *SEPERATOR = "|";

void getEvent (char* results);
void getTimeStamp(char* timeStamp, int timeStampSize);
void stringAppend(char* str1, char* str2);

int main (int argc, char *argv[])
{
  int i = 0; 
  while(1)
  { 
    i++;
    printf("%i", i);    

    char* events= realloc(NULL, 1); 
    events[0] = '\0';
    getEvent(events);

    char* timestamp= realloc(NULL, 20);
    timestamp[0] = '\0';
    getTimeStamp(timestamp, 20);

    printf("%s", events);
    printf("timestamp: %s\n", timestamp);

    free(events);
    free(timestamp);
  } 
}

void getEvent (char* results)
{
  stringAppend(results, "a111111111111");
  stringAppend(results, "b2222222222222");
}

void getTimeStamp(char* timeStamp, int timeStampSize)
{
  struct tm *ptr;
  time_t lt;
  lt = time(NULL);
  ptr = localtime(&lt);
  int r = strftime(timeStamp, timeStampSize, "%Y-%m-%d %H:%M:%S", ptr);
}

void stringAppend(char* str1, char* str2)
{   
  int arrayLength = strlen(str1) + strlen(str2) + strlen(SEPERATOR) + 1;
  printf("--%i--",arrayLength);

  str1 = realloc(str1, arrayLength);
  if (str1 != NULL)
  {
    strcat(str1, SEPERATOR);
    strcat(str1, str2);
  }
  else
  {
    printf("UNABLE TO ALLOCATE MEMORY\n");
  }
}

person John Soer    schedule 07.07.2010    source источник
comment
почему вы распределяете память каждый раз в цикле, а не просто заранее распределяете и повторно используете память?   -  person Mark Elliot    schedule 07.07.2010
comment
игнорируйте, я вижу - далее (к комментарию Марка) realloc выделяет только один байт для каждого выделения   -  person KevinDTimm    schedule 07.07.2010
comment
Я не знаю, сколько текста возвращает getEvent. Я инициализирую указатель char на один символ и передаю его функции getEvent, которая вызывает stringAppend для увеличения размера указателя char в зависимости от того, сколько текста ему нужно добавить. В некоторых случаях getEvent возвращает 20 символов, в некоторых случаях строка может превышать мегабайт.   -  person John Soer    schedule 07.07.2010
comment
Почему realloc(NULL, foo) вместо malloc(foo)?   -  person zneak    schedule 07.07.2010


Ответы (5)


Проблема в том, что, хотя stringAppend перераспределяет указатели, только stringAppend знает об этом факте. Вам нужно изменить stringAppend, чтобы он принимал указатели на указатели (char **), чтобы исходные указатели обновлялись.

person Josh Matthews    schedule 07.07.2010
comment
В качестве альтернативы он может просто вернуть новое значение str1 (поскольку он не пытается перераспределить str2). - person caf; 07.07.2010

Вы перераспределяете str1, но не передаете значение из своей функции, поэтому потенциально измененный указатель утекает, а старое значение, которое было освобождено realloc, снова освобождается вами. Это вызывает предупреждение о двойном освобождении.

person Matt Curtis    schedule 07.07.2010

Эта строка в stringAppend:

str1 = realloc(str1, arrayLength);

изменяет значение локальной переменной в stringAppend. Эта локальная переменная с именем str1 теперь указывает либо на перераспределенную память, либо на NULL.

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

person Windows programmer    schedule 07.07.2010

Все комментарии были очень полезны. Конечно, вполне понятно, почему произошла ошибка. Я решил это, внося следующие изменения.

И для getEvent, и для stringAppend я возвращаю указатель char.

e.g.

char* stringAppend(char* str1, char* str2) 
{    
  int arrayLength = strlen(str1) + strlen(str2) + strlen(SEPERATOR) + 1; 
  printf("--%i--",arrayLength); 

  str1 = realloc(str1, arrayLength); 
  if (str1 != NULL) 
  { 
    strcat(str1, SEPERATOR); 
    strcat(str1, str2); 
  } 
  else 
  { 
    printf("UNABLE TO ALLOCATE MEMORY\n"); 
  } 
  return str1;
} 
person John Soer    schedule 07.07.2010

Это не ответ на ваш вопрос (и он вам не нужен, поскольку ошибка была указана), но у меня есть другие комментарии по поводу вашего кода:

char* events= realloc(NULL, 1); 
events[0] = '\0';

Вы не проверяете, что realloc успешно выделенная память.

char* timestamp= realloc(NULL, 20);
timestamp[0] = '\0';

Здесь та же проблема. В этом случае вам вообще не нужен realloc. Поскольку это буфер фиксированного размера, вы можете использовать только:

char timestamp[20] = "";

И не делайте этого:

str1 = realloc(str1, arrayLength);

потому что, если realloc выйдет из строя, вы потеряете память, на которую str1 указывал раньше. Вместо:

char* temp = realloc(str1, arrayLength);
if (temp != NULL)
{
    str1 = temp;
    ...
}

Обратите внимание: поскольку вы изменяете stringAppend для возврата новой строки, вам следует выполнить аналогичные проверки в вызывающих функциях.

Кроме того, «разделитель» пишется с двумя As, а не с двумя Es.

person jamesdlin    schedule 07.07.2010