realloc не может расширить массив символов при передаче ввода из файла

Я написал приведенную ниже функцию c, чтобы получить строку от пользователя. Он использует realloc для динамического изменения размера массива символов, чтобы приспособиться к неизвестной длине массива символов. Насколько я понимаю, он должен иметь возможность принимать столько входных данных, сколько вы можете ему передать (или иметь доступную память), однако, когда я пытаюсь передать ему текст из рандомизированного текстового файла (используется "tr '\n' ' ' ./random.txt", чтобы убедиться, что я удалил все новые строки из текстового файла), я получаю сообщение "Невозможно выделить память для хранения массива символов. Выход!" сообщение об ошибке. Почему это происходит? Должен ли мой массив вмещать до гигабайт данных, поскольку у меня 16 гигабайт ОЗУ, так как он был разработан для динамического роста?

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

void GetString(int*, int*);

int main(void)
{
    unsigned int strLength = 32;
    char *stringPtr = malloc(strLength);
    if (stringPtr == NULL)
    {
        fprintf(stderr, "Unable to allocate memory to hold char array. Exiting!\n");
        return 1;
    }
    printf("Enter some input: ");
    int c = EOF;
    unsigned int i = 0;
    while ((c = getchar()) != '\n' && c != EOF)
    {
        stringPtr[i++] = (char) c;
        if (i == strLength)
        {
            strLength *= strLength;
            if ((stringPtr = realloc(stringPtr, strLength)) == NULL)
            {
                fprintf(stderr, "Unable to expand memory to hold char array. Exiting!\n");
                return 2;
            }
        }
    }
    stringPtr[i] = '\0';
    if (sizeof(stringPtr) < strLength)
    {
        stringPtr = realloc(stringPtr, i);
    }
    printf("\n\nString value: %s\n\n\n", stringPtr);
    free(stringPtr);
    stringPtr = NULL;
}

person Anthony Hopkins    schedule 14.10.2014    source источник
comment
Попробуйте изменить unsigned int strLength на size_t strLength. Кроме того, убедитесь, что вы компилируете в 64-битном режиме, если вы хотите использовать более 2-3 ГБ памяти в одном процессе.   -  person zwol    schedule 14.10.2014
comment
strLength *= strLength; растет быстро, может быть strLength *= 2;?   -  person chux - Reinstate Monica    schedule 14.10.2014
comment
Файл, который я пытаюсь использовать, в настоящее время имеет размер 2 МБ, поэтому я не использую столько памяти, и он все еще выдает ту ошибку, которую я нахожу странной. Я использую make, который в настоящее время настроен на компиляцию в 64-битном режиме. Спасибо за предложение и быстрый ответ!   -  person Anthony Hopkins    schedule 14.10.2014
comment
@chux: Это сработало! Это была простая математика, которую я проглядел... Это довольно неловко.... Большое спасибо!   -  person Anthony Hopkins    schedule 14.10.2014


Ответы (1)


Я немного изменил вашу программу, чтобы помочь понять, что происходит не так:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main(void)
{
    unsigned int strLength = 32;
    char *stringPtr = malloc(strLength);
    if (!stringPtr)
    {
        fprintf(stderr, "failed to allocate %u bytes: %s\n",
                strLength, strerror(errno));
        return 1;
    }
    int c = EOF;
    unsigned int i = 0;
    while ((c = getchar()) != '\n' && c != EOF)
    {
        stringPtr[i++] = (char) c;
        if (i == strLength)
        {
            unsigned int nStrLength = strLength;
            nStrLength *= nStrLength;
            if (nStrLength <= strLength)
            {
                fprintf(stderr, "cannot grow string of %u bytes any more\n",
                        strLength);
                return 1;
            }
            if ((stringPtr = realloc(stringPtr, nStrLength)) == NULL)
            {
                fprintf(stderr,
                        "failed to enlarge string from %u to %u bytes: %s\n",
                        strLength, nStrLength, strerror(errno));
                return 1;
            }
            strLength = nStrLength;
        }
    }
    return 0;
}

Когда я запускаю более или менее, как вы, это то, что я получаю:

$ yes | tr -d '\n' | ./a.out 
cannot grow string of 1048576 bytes any more

1048576 — это один мегабайт, но, что более важно, это 220. Квадрат 220 равен 240, что больше, чем 232 – 1 — наибольшее значение, которое может быть представлено в unsigned int в этой системе. Я предсказываю, что вы получите те же результаты на вашей системе.

Поэтому я рекомендую вам внести три изменения:

  • Как я уже упоминал, все эти переменные unsigned int вместо этого должны быть size_t.
  • Как уже упоминалось, измените свой код, чтобы просто умножать strLength на два, а не само по себе.
  • Включите явную проверку на переполнение по аналогии с тем, что я сделал здесь. Или принять reallocarray, что, вероятно, не t в вашей библиотеке C, но вы можете перейти по ссылке. [EDIT: reallocarray в целом все еще хорошая идея, но она не помогает с этим класс ошибок числового переполнения, потому что переполняется количество элементов в массиве, а не произведение количества элементов и размера.]

Кроме того, на этот раз это не было вашей непосредственной проблемой, но на будущее: strerror(errno) — ваш друг. Всегда печатайте strerror(errno) при сбое системного примитива.

person zwol    schedule 14.10.2014
comment
Большое спасибо за подробное объяснение! У меня есть еще немного исследований, чтобы сделать, по-видимому. Это был мой первый реальный опыт работы с указателями и выделением памяти помимо функций подкачки и других простых примеров. Это помогло многое прояснить! Проголосуйте за ваше дальнейшее объяснение. - person Anthony Hopkins; 14.10.2014