программа, использующая динамическое выделение памяти для получения строки неизвестного размера на c

#c #input #dynamic-memory-allocation

Вопрос:

Я создаю программу,в которой пользователь будет вводить строку неизвестного размера, если пользователь вводит «КОНЕЦ», то программа завершится, в противном случае она запросит другую строку и другую, поскольку размер строки неизвестен, я использовал динамическое распределение памяти, чтобы взять строку неизвестного размера, вот что я пробовал

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

int main() 
{
    bool on = true;
    char *string;
    while(on)
    {
        string = malloc(sizeof(char));
        int i;
        for (i = 0;; i  )
        {
            int n;
            n = getchar();
            if (n == 'n')
            {
                break;
            }
            else
            {
                string = realloc(string, sizeof(char));
                string[i] = (char)n;
            }
        }
        string[i] = '';
        if (strcmp(string, "END") == 0)
        {
            on = false;
        }
        printf("Len: [%i],String = %sn",i 1,string);
        free(string);
    }
    return 0;
}
 

но если я ввожу строку размером более 25 или около того, программа выходит из строя, она выдает мне этот вывод

 0 [main] get_the_line_dynamically 1056 cygwin_exception::open_stackdumpfile: Dumping stack trace to get_the_line_dynamically.exe.stackdump
 

но что не так с этим кодом??

Комментарии:

1. Ваш realloc вызов не увеличивает буфер; вы входите sizeof(char) , тем самым запрашивая «пожалуйста, измените размер этого на 1», что уже есть.

2. Ты, наверное, хотел realloc(string, i 2) . Например, предположим, что вы прочитали ровно один символ ввода. В этом случае тело цикла выполняется один раз с i == 0 помощью . Буфер уже имеет размер 1, и поэтому его нужно увеличить до 2: для хранения символа at string[0] и завершающего байта null требуется два байта string[1] .

3. @ShahzadIftikhar sizeof(char) гарантированно будет 1.

4. Используйте тип size_t для i , не int , и следите за тем, чтобы он не обернулся или не стал слишком большим. Это хорошая идея, чтобы проверить возвращаемое значение malloc и realloc для null.

5. @Kaz, нет, это не очень хорошая идея для использования sizeof(char) , так как это ничего не упрощает — вам все равно придется найти все места, где sizeof(char) это сделано, и выяснить, нужно ли их менять или нет. Если вы хотите улучшить свою программу в будущем, вы можете использовать sizeof(*string) .

Ответ №1:

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

Таким образом, вам нужно :

 string = realloc( string, i   2 ) ;
 

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

 #define STRING_CAPACITY_INCREMENT 32
size_t string_capacity = STRING_CAPACITY_INCREMENT ;
char* string = malloc( string_capacity ) ;
 

Затем:

 // If insufficient capacity for new character plus NUL...
if( i   2 > string_capacity )
{
    string_capacity  = STRING_CAPACITY_INCREMENT ;
    string = realloc(string, string_capacity );
}
string[i] = (char)n;
 

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

В некоторых случаях вы можете выбрать экспоненциальное увеличение емкости, например удвоение: 8, 16, 32, 64 и т.д. до некоторого разумного предела, после чего он становится линейным. Это может быть лучше с точки зрения балансировки производительности и использования памяти, если у вас много маленьких строк и всего несколько длинных.

Комментарии:

1. примечание по правописанию: «имейте в виду», буквально, чтобы иметь в виду.

2. @TimRandall исправил — больше опечаток, чем незнания. Хорошее место.

3. Приращения в один байт действительно кое-что спасают: сложность кода. Рост обрабатывается одной realloc строкой, выданной безоговорочно.

4. @Kaz Правда, в этом тривиальном примере это менее сложно, и производительность, возможно, не является проблемой, если ввод осуществляется вручную, а строки короткие. Однако в любом значимом приложении это не было бы привычкой, которую вы хотели бы применять повсеместно. Проблема, если сложность легко решается, например, типом структуры строкового буфера и функцией обертывания расширения. Но на самом деле именно поэтому существует C .