Динамическое выделение памяти для массива символов в соответствии с размером пользовательского ввода — без использования malloc, realloc, calloc и т. д.

Проблема в том, что я хотел бы динамически распределять память в соответствии с размером ввода пользователя. Обратите внимание, что пользователь не указывает размер ввода. Мы должны вычислить, как долго это было, а затем выделить для него только точное количество памяти. Я ищу что-то вроде этого:

char* input_str = NULL; // No initial size whatsoever
cout<<"\nEnter the string: ";
//Determine how long the string entered was, and allocate memory for it
//make input_str point to it. 

Может быть, это поможет нам написать нашу версию std::string?


c++
person Manjunath Reddy    schedule 16.04.2015    source источник
comment
если вы застряли с типом char, вы можете посмотреть на более C-ish решение: stackoverflow.com/a/8164021/420446   -  person willll    schedule 16.04.2015


Ответы (4)


Насколько я понимаю вопрос, в частности может быть это может помочь нам написать нашу версию std::string , речь идет о

  • делать то, что делает std::getline из заголовка <string>, чтобы увидеть, что это включает.

Это уже хорошо обсуждалось Бьерном Страуструпом в его статье Learning Standard C++ as a New Language, за исключением того, что Бьерн обсуждает ввод с помощью оператора >>, который вводит только одно слово, разделенное пробелами.

Бьерн начинает с псевдокода для гипотетического студенческого упражнения:

напишите подсказку "Пожалуйста, введите свое имя"
прочитайте имя
напишите "Здравствуйте, ‹имя›"

Затем он представляет одно возможное решение C++:

#include<iostream>      // get standard I/O facilities
#include<string>        // get standard string facilities

int  main()
{
    using namespace std;    // gain access to standard library

    cout << "Please enter your first name:\n";
    string name;
    cin >> name;
    cout << "Hello " << name << '\n';
}

И после некоторого обсуждения он представляет решение в стиле C, самодельную программу в стиле C, которая делает то же самое, что и решение в стиле C++:

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

void quit()  // write error message and quit
{
    fprintf(stderr," memory exhausted\n") ;
    exit(1) ;
}

int main()
{
    int  max = 20;
    char* name = (char*) malloc(max) ;  // allocate buffer
    if (name == 0) quit();
    printf("Please enter your first name:\n");
    while (true) {  // skip leading whitespace
        int c = getchar();
        if (c ==  EOF) break;  // end of file
        if (!isspace(c)) {
            ungetc(c,stdin);
            break;
        }
    }
    int i = 0;
    while (true) {
        int c = getchar() ;
        if (c == '\n' || c ==  EOF) {  // at end; add terminating zero
            name[i] = 0;
            break;
        }
        name[i] = c;
        if (i== max-1) {  // buffer full
            max =  max+max;
            name = (char*)realloc(name, max) ; // get a new and larger buffer
            if (name == 0) quit() ;
        }
        i++;
    }
    printf("Hello %s\n",name);
    free(name) ;  // release memory
    return 0;
}

Эти две программы не совсем эквивалентны: первая программа в стиле C++ считывает только одно слово ввода, а программа C пропускает пробелы, а затем считывает всю строку ввода. Но это иллюстрирует, что нужно для того, чтобы сделать это самостоятельно. Короче говоря, лучше используйте стиль C++. ;-)

person Cheers and hth. - Alf    schedule 16.04.2015
comment
Способность версии C использовать realloc является (почти фундаментальной? Из-за недостатков модели распределителя C++) преимуществом производительности, которого не хватает версии C++. - person Yakk - Adam Nevraumont; 16.04.2015
comment
@Yakk: Да. Насколько я помню, Говард Хиннант экспериментировал с использованием realloc для реализации std::vector. Хотя, возможно, это был кто-то другой. В результате вы не можете получить это формально правильно, но вы значительно повышаете эффективность. ;-) - person Cheers and hth. - Alf; 16.04.2015
comment
Хорошая работа, на самом деле заметил, что спрашивающий упомянул о реализации своего собственного std::string в своем вопросе. - person Joseph Mansfield; 16.04.2015

Просто используйте std::string, который сделает все это за вас:

std::cout << "\nEnter the string: ";

std::string input;

// To get a word:
std::cin >> input;

// To get a line of input:
std::getline(std::cin, input);

Вам вообще не нужно будет беспокоиться о распределении памяти.

Внутри эти функции будут вызывать std::cin.rdbuf(), чтобы получить доступ к базовому буферу потока и читать посимвольно, пока не будет выполнено условие остановки. При этом он увеличивает внутреннюю память, а не выделяет (де)распределяет с помощью new[] и delete[].

Альтернативный простой (но ограниченный и потенциально опасный) подход, который вы можете использовать, заключается в выделении буфера такого размера, который вам когда-либо понадобится (что-то вроде new char[100], и считывание входных данных в него (что-то вроде std::cin.read или std::cin.getline, и т. д.). Затем вы можете определить, сколько символов было вставлено в буфер, выделить некоторое хранилище нужного размера, перенести ввод в это хранилище, а затем освободить старый буфер. При таком подходе вы должны быть особенно осторожны, чтобы избегайте переполнения буфера, которые могут привести к уязвимостям безопасности вашей программы, и вы, конечно, ограничены определенным максимальный размер ввода.

person Joseph Mansfield    schedule 16.04.2015
comment
Джозеф: это самое простое решение - да; однако есть ли способ точно определить размер ввода и выделить массив самостоятельно, пытаясь таким образом создать собственную версию std::string ? - person Manjunath Reddy; 16.04.2015
comment
@ManjunathReddy Добавлены некоторые примечания о том, как это можно реализовать вручную. - person Joseph Mansfield; 16.04.2015
comment
@ManjunathReddy: я не понимаю, как использование адресов std::string Может быть, это может помочь нам написать нашу версию std::string, можете ли вы уточнить это? - person Cheers and hth. - Alf; 16.04.2015
comment
Альф: Принял это из-за полезных заметок о ручной реализации. - person Manjunath Reddy; 17.04.2015

Выделите массив с пространством для 1 или более символов. Считайте символы из вашего источника ввода в массив, пока в массиве не закончится место или вы не достигнете своего завершающего символа. Если вам не хватило места, выделите новый массив вдвое большего размера, скопируйте символы и освободите старый массив. Затем снова вернитесь к чтению символов. Повторяйте, пока не достигнете конечного символа (или любого другого условия, на котором вы хотите остановиться).

person Benjamin Lindley    schedule 16.04.2015
comment
Этот ответ — единственный, который после решения std::string описывает правильный способ чтения неизвестного потока символов в память. Единственное дополнение (если вы хотите промышленного качества) - это, возможно, иметь параметр максимальной длины (в мегабайтах), чтобы иметь дело с возможностью того, что кто-то попытается исчерпать пространство вашей памяти и вызвать неожиданное завершение работы и/или переключиться на сегментированные строки ( которые обрабатывают очень большие строки намного лучше, чем непрерывные строки). Но это выходит за рамки данной проблемы. - person Yakk - Adam Nevraumont; 16.04.2015

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

Обратите внимание, что, поскольку вы используете C++, вероятно, не имеет смысла выделять память вручную: вместо этого вы можете использовать std::string.

person Lundin    schedule 16.04.2015