Realloc не может динамически увеличить от 1 до более миллиона элементов

Считывание текстового файла с двумя столбцами и сохранение длинных значений int в массиве, который динамически перераспределяется, завершается неудачно, когда размер массива увеличивается до 200 000 перераспределений памяти.

    long int load_list_unklen(long int (**ptr), FILE *infname)
    {   long int row =0;
        char buf[64];
        while (fgets (buf, sizeof(buf), infname)) {
            // M is defined to be = 2
            *ptr = (long int *)realloc(*ptr, M * row+1 * sizeof(ptr));
            if(!ptr) {
                perror("Out of memory");
                free(ptr);
                exit( 1); }
            sscanf(buf, "%ld\t%ld", &(*ptr)[row*2],&(*ptr)[row*2+1]);
            row += 1;
        }
        if (ferror(infname)) {
            fprintf(stderr,"Oops, error reading stdin\n");
            abort();
        }
        return  row;
    }

Заметить, что buf получает строку с двумя числами, разделенными табуляцией. Код терпит неудачу, когда он пытается загрузить файл с более чем 2-миллиметровыми строками, и приращения строк останавливаются около 221181, поэтому мне интересно, есть ли здесь предел, где realloc задыхается? Должен ли я звонить по-другому?

Вот как я вызываю функцию:

long int *act_nei = (long int *)malloc(M * sizeof (act_nei) );
const long int sz  = load_list_unklen(&act_nei, fp);

Использование кода из поста SO для перераспределения слотов памяти, где мой пример для больших входных файлов.
Realloc и sscanf в функцию Realloc и scanf

4 ответа

Ваш расчет использования памяти очень неправильный.

Во-первых, сделайте вашу жизнь намного проще, не используя * ptr все время, а используя локальную переменную. подобно

long f (int* *pptr...)
{
    int* ptr = *pptr;
    ...
    *pptr = ptr;
}

Теперь становится очевидным, что ваш sizeof (ptr) неверен. Это размер int **, который совершенно не имеет значения. То, что вы хотите, это размер int, который будет sizeof(**ptr) в вашем коде или sizeof (*ptr), если вы используете локальную переменную для указателя.

Конечно, когда все совершенно не так:

M * row+1 * sizeof(ptr)

Это вычисляет M* строку, а затем добавляет 1*sizeof(ptr). Вы выделяете недостаточно места для памяти, поэтому ваше приложение скоро будет аварийно завершено.

Вы повреждаете кольцо malloc, записывая за пределы выделенного пространства. Отсутствует () и неправильный размер. Пытаться:

* ptr = (long int *) realloc (* ptr, M * (row + 1) * sizeof (** ptr));

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

#define handle_error() do{ perror("malloc"); exit(EXIT_FAILURE); } while (0)

struct pairs {
        unsigned size;
        unsigned used;
        struct { long int x; long int y; } *data;
        };

struct pairs *readpairs(FILE *fp)
{
struct pairs *ret;
char *line[80];

ret = malloc (sizeof *ret);
if (!ret) { handle_error();}

ret->size = ret->used = 0;
ret->data = NULL;

while (fgets(line, sizeof line, fp)) {
        int rc;
      if (ret->used >= ret->size) { // resize needed
                void *newp;
                unsigned newsiz;
                newsiz = ret->size ? 2* ret->size : 8;

                newp = realloc(ret->data,  newsiz * sizeof *ret->data);
                if (!newp) { handle_error();}
                ret->data = newp;
                ret->size = newsiz;
                }
        rc = sscanf(line,"%ld\t\%ld"
                , &ret->data[ret->used].x
                , &ret->data[ret->used].y
                );
        if (rc != 2) { handle_error();}
        ret->used += 1;
        }
return ret;
}

Часть из уже опубликованных комментариев о неправильном использовании sizeof() У меня есть более общий комментарий. Я бы не стал перераспределять буфер для каждой прочитанной строки.

Семантика realloc таков, что будет возвращать непрерывную область памяти запрошенного размера. В некоторых ОС, когда это возможно, это будет сделано, расширяя буфер, передаваемый в качестве аргумента "на месте". В других ОС, или если в конце переданного буфера нет свободного места, будет выделен совершенно новый буфер и скопировано в него содержимое исходного буфера. Если ваша память особенно фрагментирована, это может привести к mallocs а также memcpy под капотом.

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

Другая проблема может возникнуть из-за того, что realloc вернуть непрерывную память. В зависимости от ОС и характеристик системы у вас может не быть свободной области непрерывной памяти указанного размера, но может быть несколько областей, свободных от меньшего размера. Если такой сценарий возможен, нужно подумать, действительно ли необходим непрерывный буфер. Например, решение, все еще обеспечивающее O(1) время доступа (хотя и не такое быстрое, как непрерывный буфер), может заключаться в создании первого буфера, содержащего указатели на страницы фиксированного размера. В этом случае, что будет рассматриваться как ptr[n] в смежном случае станет ptr[n / PAGE_SIZE][n % PAGE_SIZE] в новой схеме.

Другие вопросы по тегам