2017-01-15 14 views
0

Я использую этот код для чтения файла:Ошибка при чтении файла с «fgetc» (Overflow)

char* fs_read_line(FILE* file) 
{ 
    if (file == NULL) { 
     return "CFILEIO: Error while reading the file: Invalid File"; 
    } 

    long threshold = ftell(file); 
    fseek(file, 0, SEEK_END); 
    uint8_t* buffer = calloc(ftell(file)-threshold, sizeof(uint8_t)); 

    if(buffer == NULL) 
     return; 

    int8_t _; 
    fseek(file, threshold, SEEK_SET); 

    uint32_t ct = 0; 
    while ((_ = (char)(fgetc(file))) != '\n' 
     && _ != '\0' && _ != '\r' && _ != EOF) { 
     buffer[ct++] = _; 
    } 

    buffer = realloc(buffer, sizeof *buffer * (ct + 1)); 
    buffer[ct] = '\0'; 
    return buffer; 
} 

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

Другой способ, который я пытался сделать, это realloc буфер после каждой итерации, но это не тот подход, который я хочу.

Есть ли способ динамически изменять размер массива в зависимости от текущей итерации, не всегда uisng realloc? или есть ли способ определить, как долго текущая линия использует ftell и fseek?

+1

код вы показываете не компилировать в C++. Не добавляйте несвязанные теги. –

+0

'sizeof (uint8_t *)' дает вам размер указателя, а не размер одного байта. Который почти полностью определен только 1 в системе, где 'uint8_t' существует. Таким образом, вы выделяете в 4 или 8 раз больше размера файла, а затем не проверяете, успешно ли это распределение. –

+0

Вместо 'memset',' buffer [ct ++] = _' намного яснее. –

ответ

0

Код не возвращает указатель на строку .

Нет нулевой символ в возвращаемом buffer, поэтому вызывающему коду не хватает возможности знать длину выделенной памяти. Это, безусловно, приводит к ошибке вызывающего кода.

При Перераспределение, добавьте 1.

// buffer = realloc(buffer, ct * sizeof(uint8_t*)); 
//           v--- no star 
buffer = realloc(buffer, ct * sizeof(uint8_t) + 1); 
buffer[ct] = '\0'; 

// or better 
size_t ct = 0; 
... 
buffer = realloc(buffer, sizeof *buffer * (ct + 1)); 
buffer[ct] = '\0'; 

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

Размеры массива не могут измениться. Для динамического изменения размера выделенной памяти требуется realloc(). Примечание: объем необходимой памяти может быть определен перед вызовом выделения памяти.

или есть способ, чтобы определить, как долго текущая строка с помощью ftell и fseek?

Нравится этот код, вы нашли верхнюю границу текущей длины строки. ftell и fseek не находите конец линии.

Код может «искать» до конца строки fscanf(file, "%*[^\n]"); или 1 за пределами fgetc(file).

+0

Кстати, есть ли у меня утечка памяти, сокращая выделенную память, не освобождая ее в первую очередь? – Lupor

+0

@Lupor No. Но у вас могут быть утечки в вызывающем коде - освобождает ли он указатель, возвращаемый этой функцией ?. Примечание: если память свободна, код не может использовать значение указателя. Код не может перераспределяться на основе этого указателя. – chux

+0

Да, я всегда освобождаю указатель, возвращаемый функцией. Спасибо! но последний вопрос: почему я должен использовать 'sizeof (uint8_t *)' вместо 'sizeof (uint8_t)' при вызове 'realloc'? почему я получаю junk-байты, когда doin g 'sizeof (uint8_t)'? – Lupor

0

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

  1. Вы начинаете с произвольной позиции в файле, но выделяете память так, как будто вы начинаете с начала файла. Выделить ftell(file) - threshold байт.
  2. Вы являетесь источником выделения слишком большого объема памяти. Вместо этого sizeof(uint8_t *) должен быть sizeof(uint8_t). Вы выделяете в 4 или 8 раз больше памяти, чем должны.

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

+0

по какой-то причине, если я удалю 'sizeof (uint8_t *)' at 'realloc', я получу баны junk, но я удалил' sizeof (uint8_t *) 'в исходном распределении и добавил' ftell (файл) - порог', спасибо! – Lupor

+0

Почему вы перераспределяете? – zmbq

+0

, чтобы уменьшить размер «buffer» ptr до размера фактического количества прочитанных байтов, потому что я начинаю выделять «буфер» с количеством байтов, которое больше, чем фактическая строка ^^ – Lupor

0

следующий код:

  1. чисто компилирует
  2. выполняет требуемую операцию
  3. правильно обрабатывает условия ошибки
  4. правильно объявляет типы переменных
  5. правильно возвращает char*, а не uint8_t*
  6. оставляет открытым вопрос: зачем возвращать 2x необходимый buffe r длина
  7. сообщение об ошибке, отображаемое, когда параметр передан в NULL неверен. Предложить замену на указание, переданное в указателе файла, было NULL
  8. код, опубликованный OPs, не смог проверить возвращаемое значение от каждого вызова до fseek() и не смог проверить возвращаемое значение от каждого вызова до ftell(), которое оно должно делать для обеспечения операции (s) был успешным. Я не добавил, что проверка ошибок в моем ответе, чтобы не загромождать код, однако, это должно быть выполнено.

и теперь, код:

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 


char* fs_read_line(FILE* file); 


char* fs_read_line(FILE* file) 
{ 
    if (!file) 
    { 
     return "CFILEIO: Error while reading the file: Invalid File"; 
    } 

    // implied else, valid parameter 

    long threshold = ftell(file); 
    fseek(file, 0, SEEK_END); 

    char* buffer = calloc((size_t)(ftell(file) - threshold) *2 +1, sizeof(char)); 
    if(buffer == NULL) 
     return NULL; 

    // implied else, calloc successful 

    int ch; 
    fseek(file, threshold, SEEK_SET); 

    size_t ct; 
    while ((ch = fgetc(file)) != '\n' 
     && ch != '\0' 
     && ch != '\r' 
     && ch != EOF) 
    { 
     buffer[ct++] = (char)ch; 
    } 

    return buffer; 
} // end function: fs_read_line