Как конструктор std :: string обрабатывает char [] фиксированного размера? - PullRequest
0 голосов
/ 17 октября 2018

Как строковый конструктор обрабатывает char[] фиксированного размера, когда фактическая последовательность символов в этом char[] может быть меньше максимального размера?

char foo[64];//can hold up to 64
char* bar = "0123456789"; //Much less than 64 chars, terminated with '\0'
strcpy(foo,bar); //Copy shorter into longer
std::string banz(foo);//Make a large string

В этом примере будет размерстроки banz объектов основывается на исходной длине символа * или символе [], в который она скопирована?

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

Прежде всего, вы должны помнить (или знать), что char строки в C ++ действительно называются нулевыми окончаниями байтовыми строками .Этот бит с нулевым символом в конце является специальным символом ('\0'), который сообщает конец строки.

Второе, что вы должны помнить (или знать), - это то, что массивы естественным образом затухаютуказатели на массивы первый элемент.В случае foo из вашего примера, когда вы используете foo, компилятор действительно делает &foo[0].

Наконец, если мы посмотрим, например, эту std::string ссылку на конструктор вы увидите, что существует перегрузка (номер 5), которая принимает const CharT*CharT, являющимся char для обычных char строк).

Собираем все вместе, с

std::string banz(foo);

вы передаете указатель на первый символ foo, и конструктор std::string будет обрабатывать его как завершенную нулем байтовую строку.И от нахождения нулевого терминатора он знает длину строки.Фактический размер массива не имеет значения и не используется.

Если вы хотите установить размер объекта std::string, вам нужно явно сделать это, передав аргумент длины (вариант 4 в ссылке на конструктор)):

std::string banz(foo, sizeof foo);

Это игнорирует нулевой терминатор и устанавливает длину banz равной размеру массива.Обратите внимание, что нулевой терминатор будет по-прежнему сохраняться в строке, поэтому, передавая указатель (полученный, например, с помощью функции c_str) в функцию, которая ожидает строку с нулевым символом в конце, строка будеткажется коротким.Также обратите внимание, что данные после нулевого терминатора будут неинициализированы и имеют неопределенное содержимое.Вы должны инициализировать эти данные, прежде чем использовать их, иначе у вас будет неопределенное поведение (а в C ++ даже чтение неопределенных данных - UB).


Как уже упоминалось в комментарии MSalters, UB при чтении неинициализированных и неопределенных данных также используется для создания объекта banz с использованием явного размера.Обычно он работает и не приводит к каким-либо проблемам, но нарушает правила, изложенные в спецификации C ++.

Исправить это легко, хотя:

char foo[64] = { 0 };//can hold up to 64

Выше будет инициализироваться все массива в ноль.Следующий вызов strcpy не затронет данные массива за пределами терминатора, и поэтому остаток массива будет инициализирован.

0 голосов
/ 17 октября 2018

Вызывается конструктор, который принимает const char* в качестве аргумента.Этот конструктор пытается скопировать символьные данные, на которые указывает этот указатель, до тех пор, пока не будет достигнут первый терминатор NUL.Если такого NUL-терминатора нет, поведение конструктора будет undefined .

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

Возможно, мог существовать шаблонизированный конструктор std::string, принимающий const char[N] в качестве аргумента, который позволил бы вставить более одногоСимвол NUL (класс std::string в конце концов поддерживает это), но он не был введен, и сделать это сейчас было бы серьезным изменением;использование

std::string foo{std::begin(foo), std::end(foo)};

также скопирует весь массив foo.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...