realloc терпит неудачу после нескольких вызовов, только если не отлаживает

Приведенный ниже код иногда дает сбой при вызове buffer = (char*) realloc(buffer, allocated * sizeof(char)); (отмечен ниже), который я использую для динамического выделения пространства для char*, первоначально выделяя 1 символ и удваивая выделенную сумму каждый раз, когда уже имеющейся памяти недостаточно для хранения строки.

У меня есть очень похожий код во многих других частях моего проекта с той же политикой выделения памяти и вызовами (изменение только типа void*, который я передаю на realloc).

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

Однако при вызове программы из командной строки велика вероятность того, что один из вызовов realloc через какое-то время завершится с ошибкой «Доступ к месту чтения нарушения» — правда, это происходит не постоянно, а только происходит после того, как приведенная ниже функция была вызвана несколько раз, при этом уже произошло много перераспределений.

Что еще более странно, я поместил несколько отпечатков до и после вызова realloc, чтобы подтвердить, было ли изменено местоположение указателя, и, когда я сделал это и запустил программу, вызовы realloc прекратили случайные сбои.

Что я делаю неправильно?

TOKEN
next_token_file(FILE* file, 
                STATE_MACHINE* sm, 
                STATE_MACHINE* wsssm)
{
    char* buffer = (char*) malloc(sizeof(char));
    size_t allocated = 1;
    size_t i = 0;
    while(1)
    {
    /*
    ... code that increments i by one and messes with sm a bit. Does nothing to the buffer.
    */
        // XXX: This fails when using realloc. Why?
        if(i + 1 >= allocated)
        {
            allocated = allocated << 1;
            buffer = (char*) realloc(buffer, allocated * sizeof(char));
        }
        buffer[i] = sm->current_state->state;
    /*
    ... more code that doesn't concern the buffer
    */
    }
    // Null-terminate string.
    buffer[++i] = 0;
    TOKEN t = {ret, buffer};
    return t;
}

person Renan Gemignani    schedule 20.08.2013    source источник
comment
Разве это size_t allocated = 1; не должно быть size_t allocated = 16;?   -  person alk    schedule 20.08.2013
comment
Это мое предположение, либо он действительно хочет перейти с 16 символов на 2 в первом раунде (что было бы... странно).   -  person WhozCraig    schedule 20.08.2013


Ответы (3)


Из-за этих строк

char* buffer = (char*) malloc(16 * sizeof(char));
size_t allocated = 1;

программа сжимает buffer для первых 4 перераспределений. Таким образом, программа пишет в нераспределенную память, начиная с i=16, что является неопределенным поведением, так что может случиться что угодно. Кроме того, это, скорее всего, нарушает управление памятью, что, в свою очередь, приводит к сбою realloc().

Вы можете изменить эти две строки на:

size_t allocated = 16; /* or = 1 if the 16 was a typo. */
char * buffer = malloc(allocated); 

Другие примечания:

Ссылаясь на последнее примечание, следует применить следующие модификации.

char * buffer = malloc(allocated); 

может стать:

char * buffer = malloc(allocated); 
if (NULL == buffer)
{
  /* Error handling goes here. */
}

и

buffer = (char*) realloc(buffer, allocated * sizeof(char));

может стать:

{
  char * pctmp = realloc(buffer, allocated);
  if (NULL == pctmp)
  {
    /* Error handling goes here. */
  }
  else
  {
    buffer = pctmp;
  }
}
person alk    schedule 20.08.2013
comment
sizeof (char) даже основан на c99, так как он должен быть 1 байт, так где вы ссылаетесь на этот sizeof (char) всегда 0? - person dhein; 20.08.2013
comment
@Zaibis: Ой ...0 была опечатка. Фиксированный. - person alk; 20.08.2013
comment
Обработка отказа realloc — такая распространенная ошибка, использование одного и того же указателя приводит к утечке :) - person paulm; 20.08.2013
comment
Теперь, с этим, у меня все еще остается одно сомнение: почему этот вызов не работает только тогда, когда я не отлаживаю его, а не постоянно падаю? - person Renan Gemignani; 20.08.2013
comment
@RenanGemignani: Вы вот-вот осознаете иррациональность неопределенного поведения. - person alk; 20.08.2013
comment
У меня похожая случайная проблема с realloc(), я пытаюсь понять этот пример, чтобы узнать, где я делаю неправильно... но здесь я не вижу проблемы. Это правда, что выделенная часть инициализируется значением 1, а буфер инициализируется длиной 16, но вскоре, когда (i + 1 > 1), функция realloc() выравнивает длину буфера и выделенную переменную с одинаковым значением 2. Разве это не так? Это? А когда i+1 > 16, то код меняет размер буфера на 32? Или нет? Я смущен. Кто-нибудь может объяснить, пожалуйста? - person massi; 04.07.2020

Скорее комментарий, чем ответ, но у меня нет 50 баллов для комментария.

Этот:

char* buffer = (char*) malloc(16 * sizeof(char));

должно быть

char* buffer = (char*) malloc(1 * sizeof(char));

or

allocated = 16.
person Charlie Burns    schedule 20.08.2013
comment
Не применяйте результат malloc/calloc/realloc, поскольку в этом нет необходимости и не рекомендуется: stackoverflow.com/a/605858/694576 - person alk; 20.08.2013

Я не знаю, когда вы увеличиваете или уменьшаете i. Но я готов поспорить, согласно этому фрагменту, ваша проблема заключается в том, что вы бесконечно перераспределяете ресурсы, и, поскольку вы не проверяете realloc, возвращает NULL, это приведет к сбою вашей программы;)

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

Или, если вы пытаетесь работать, если возвращается недопустимое значение (что происходит, когда возвращается NULL, что может произойти, потому что вы его не проверяете) Или если вы запрашиваете область с нулевым размером (параметр размера равен 0), и вы получаете ненулевой указатель и вы работаете с этим. Но второй случай, вероятно, не произойдет в вашей программе;)

person dhein    schedule 20.08.2013