Как читать неограниченное количество символов в C

Как прочитать неограниченное количество символов в переменной char* без указания размера?

Например, скажем, я хочу прочитать адрес сотрудника, который также может занимать несколько строк.


person PrithviRaj    schedule 24.03.2010    source источник
comment
Ответы ниже демонстрируют механику проблемы, и я рекомендую вам изучить их. Одной из распространенных реализаций является getline.   -  person dmckee --- ex-moderator kitten    schedule 24.03.2010
comment
Вы должны начать с обеспечения неограниченной памяти вашего оборудования!   -  person theglauber    schedule 14.09.2012


Ответы (3)


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

char *buffer;
size_t num_read;
size_t buffer_size;

buffer_size = 100;
buffer = malloc(buffer_size);
num_read = 0;

while (!finished_reading()) {
    char c = getchar();
    if (num_read >= buffer_size) {
        char *new_buffer;

        buffer_size *= 2; // try a buffer that's twice as big as before
        new_buffer = realloc(buffer, buffer_size);
        if (new_buffer == NULL) {
            free(buffer);
            /* Abort - out of memory */
        }

        buffer = new_buffer;
    }
    buffer[num_read] = c;
    num_read++;
}

Это только что пришло мне в голову и может (читай: вероятно, будет) содержать ошибки, но должно дать вам хорошее представление.

person Dean Harding    schedule 24.03.2010
comment
@Codeka - вам следует избегать x = realloc(x, newsize); Если realloc не работает, вы теряете исходный указатель и происходит утечка памяти. Тем не менее, одно исключение из этого правила — это нормально, если ваша политика при сбое выделения ресурсов заключается в завершении процесса. - person R Samuel Klatchko; 24.03.2010
comment
Но будьте осторожны... если realloc не работает, вы упустили предыдущий указатель буфера. Нужно сделать что-то вроде void *sav=ptr; if((ptr=realloc(ptr,newsiz))==null) { free(sav); } - person Lawrence Dol; 24.03.2010
comment
Спасибо, ребята, это правильно. Я обновлю свой пример... Прошло некоторое время с тех пор, как я использовал прямой C :) - person Dean Harding; 24.03.2010
comment
если он доступен, вы можете продолжать использовать asprintf() на том же выделенном адресе для автоматического увеличения, пока вы добавляете только что прочитанное к тому, что уже находится в выделенном буфере. Я не публикую это как ответ, так как это не стандартная функция, но она широко доступна. Обычно он «просто обрабатывает» проблемы, которые могут возникнуть из-за сбоя realloc(), по крайней мере, реализация GNU. - person Tim Post♦; 24.03.2010
comment
Но все же, если пользователь вводит только несколько символов, скажем, вы выделили 100 байт, но пользователь ввел только 10 байт, остальные будут потрачены впустую. - person PrithviRaj; 24.03.2010
comment
pritviraj: Вы можете дополнить его вызовом realloc(buffer, num_read);, чтобы обрезать выделенный буфер до точного требуемого размера. - person caf; 24.03.2010
comment
Кроме того, смешивание объявлений в коде не всегда переносимо. Поскольку в данном случае это просто оптимизация, я предлагаю переместить *new_buffer рядом с объявлением *buffer. В ОП не указана платформа или стандарт. - person Tim Post♦; 24.03.2010
comment
Как правило, вы не хотите прерывать работу, если пользовательский ввод приводит к нехватке памяти. Большинство пользователей хотят получить сообщение об ошибке, не теряя при этом всех своих данных. Вместо Abort - out of memory должно быть return error code. - person Gabe; 24.03.2010
comment
Является ли приведенный ниже код правильным: если есть какие-либо изменения, исправьте его. 'целое я = 0; символ с; char *str = (char *) malloc (sizeof (char)); while((c = getchar()) != '\n') { str[i++] = c; if(i ›= strlen(str)-1) str = (char *)realloc(str, strlen(str) + 2); } стр[i] = '\0'; вернуть ул;' - person PrithviRaj; 24.03.2010
comment
Извините за публикацию кода в неправильном формате. Я новичок на этом сайте, пожалуйста, подскажите, как разместить фрагмент кода. - person PrithviRaj; 24.03.2010
comment
@Tim Post: объявление new_buffer находится в начале составного оператора, поэтому оно полностью переносимо на все версии стандартного C. - person caf; 25.03.2010
comment
@prithviraj: Фрагменты кода должны идти внутри обратных кавычек (или в вопросах с отступом в 4 пробела). Этот код неверен, BTW - вы не можете использовать strlen() для этой цели, вам нужно использовать переменную для отслеживания текущего выделенного размера (например, buffer_size в этом ответе). - person caf; 25.03.2010

Просто должен был ответить Ex7.1, стр. 330 из Beginning C, Ivor Horton, 3-е издание. Потратил пару недель на отработку. Позволяет вводить числа с плавающей запятой без указания заранее, сколько чисел введет пользователь. Сохраняет числа в динамическом массиве, а затем распечатывает числа и среднее значение. Использование Code::Blocks с Ubuntu 11.04. Надеюсь, поможет.

/*realloc_for_averaging_value_of_floats_fri14Sept2012_16:30  */

#include <stdio.h>
#include <stdlib.h>
#define TRUE 1

int main(int argc, char ** argv[])
{
    float input = 0;
    int count=0, n = 0;
    float *numbers = NULL;
    float *more_numbers;
    float sum = 0.0;

    while (TRUE)
    {
        do
        {
            printf("Enter an floating point value (0 to end): ");
            scanf("%f", &input);
            count++;
            more_numbers = (float*) realloc(numbers, count * sizeof(float));
            if ( more_numbers != NULL )
            {
                numbers = more_numbers;
                numbers[count - 1] = input;
            }
            else
            {
                free(numbers);
                puts("Error (re)allocating memory");
                exit(TRUE);
            }
        } while ( input != 0 );

        printf("Numbers entered: ");
        while( n < count )
        {
            printf("%f ", numbers[n]);  /* n is always less than count.*/
            n++;
        }
        /*need n++ otherwise loops forever*/
        n = 0;
        while( n < count )
        {
            sum += numbers[n];      /*Add numbers together*/
            n++;
        }
        /* Divide sum / count = average.*/
        printf("\n Average of floats = %f \n", sum / (count - 1));
    }
    return 0;
}

/* Success Fri Sept 14 13:29 . That was hard work.*/
/* Always looks simple when working.*/
/* Next step is to use a function to work out the average.*/
/*Anonymous on July 04, 2012*/
/* http://www.careercup.com/question?id=14193663 */
person Malcolm Jackson    schedule 14.09.2012
comment
Хорошая попытка! Пара советов по производительности - старайтесь избегать многих реаллоков, они включают копирование всего вокруг. Вместо этого перераспределите в 2 или 4 раза и подсчитывайте как доступное, так и используемое пространство. Кроме того, среднее значение можно рассчитать во время выполнения без предварительного сохранения. - person qdot; 26.09.2012

Как насчет того, чтобы просто поместить в стек буфер размером 1 КБ (или 4 КБ), считывая его, пока не найдете конец адреса, а затем выделить буфер нужного размера и скопировать в него данные? Как только вы возвращаетесь из функции, буфер стека исчезает, и у вас есть только один вызов malloc.

person tomlogic    schedule 24.03.2010
comment
Что происходит, когда адрес превышает размер буфера в стеке 1 КБ или 4 КБ? - person Gabe; 24.03.2010
comment
@gabe: Как написать адрес размером 4 КБ на конверте? - person tomlogic; 25.03.2010
comment
Незнание размера входной строки и попытка прочитать ее в буфер фиксированного размера является источником невыразимых проблем с безопасностью в коде C. - person Gabe; 25.03.2010
comment
@gabe: fgets имеет параметр размера буфера. В стандартной библиотеке C определенно есть функции, которых следует избегать (например, gets). Использование функций с ограниченной длиной и буферов фиксированного размера кажется мне довольно безопасным. - person tomlogic; 25.03.2010
comment
Буферы фиксированного размера безопасны, если вы используете функции ввода-вывода, которые имеют параметры для размера буфера. Проблема в том, что происходит, когда нужные вам данные не помещаются в ваш буфер. Вопрос заключался в том, как читать неограниченное количество символов. Не сработает ли программа из-за того, что часть адреса все еще остается во входном потоке? - person Gabe; 25.03.2010