Строка c_str() против данных ()
Я прочитал несколько мест, что разница между c_str()
а также data()
(в STL и других реализациях) заключается в том, что c_str()
всегда заканчивается нулем data()
не является. Насколько я видел в реальных реализациях, они либо делают то же самое, либо data()
звонки c_str()
,
Что мне здесь не хватает? Какой из них более правильно использовать в каких сценариях?
6 ответов
Документация верна. использование c_str()
если вы хотите строку с нулевым символом в конце.
Если разработчики произойдут реализовать data()
с точки зрения c_str()
вам не нужно беспокоиться, все еще использовать data()
если вам не нужна строка с завершающим нулем, в некоторых реализациях может оказаться, что она работает лучше, чем c_str().
Строки не обязательно должны состоять из символьных данных, они могут быть составлены из элементов любого типа. В тех случаях data()
более значимым. c_str()
по моему мнению, это действительно полезно, когда элементы вашей строки основаны на символах.
Дополнительно: в C++11 и далее обе функции должны быть одинаковыми. т.е. data
теперь должен быть нулевым. Согласно cppreference: " Возвращенный массив завершается нулем, то есть data() и c_str () выполняют одну и ту же функцию".
В C++ 11 / C++ 0x, data()
а также c_str()
больше не отличается. И поэтому data()
также должен иметь нулевое завершение в конце.
21.4.7.1
basic_string
аксессоры [string.accessors]
const charT* c_str() const noexcept;
const charT* data() const noexcept;
1 Возвращает: указатель p такой, что
p + i == &operator[](i)
для каждогоi
в[0,size()]
,
21.4.5 Доступ к элементу basic_string [string.access]
const_reference operator[](size_type pos) const noexcept;
1 Требуется: pos <= size(). 2 Возвращает:
*(begin() + pos) if pos < size()
в противном случае ссылка на объект типа T со значениемcharT();
указанное значение не должно изменяться.
Даже если вы знаете, что вы видели, что они делают то же самое, или что.data () вызывает.c_str(), неверно полагать, что это будет иметь место для других компиляторов. Также возможно, что ваш компилятор изменится в будущем выпуске.
2 причины использовать std:: string:
std:: string может использоваться как для текстовых, так и для произвольных двоичных данных.
//Example 1
//Plain text:
std::string s1;
s1 = "abc";
//Example 2
//Arbitrary binary data:
std::string s2;
s2.append("a\0b\0b\0", 6);
Вам следует использовать метод.c_str(), когда вы используете вашу строку в качестве примера 1.
Вам следует использовать метод.data (), когда вы используете свою строку в качестве примера 2. Не потому, что в этих случаях опасно использовать.c_str(), а потому, что для вас более очевидно, что вы работаете с двоичными данными для других проверяющих ваш код.
Возможная ловушка при использовании.data ()
Следующий код неверен и может вызвать ошибку в вашей программе:
std::string s;
s = "abc";
char sz[512];
strcpy(sz, s.data());//This could crash depending on the implementation of .data()
Почему для реализаторов характерно, чтобы.data () и.c_str() делали одно и то же?
Потому что это эффективнее. Единственный способ заставить.data () возвращать что-то, что не завершено нулем, это сделать так, чтобы.c_str() или.data () копировали свой внутренний буфер или просто использовали 2 буфера. Наличие одного буфера с нулевым символом в конце всегда означает, что вы всегда можете использовать только один внутренний буфер при реализации std::string.
Все предыдущие комментарии являются консистенцией, но я также хотел бы добавить, что начиная с C++17, str.data() возвращает char* вместо const char*
На это уже отвечено несколько замечаний о цели: Свобода реализации.
std::string
операции - например, итерация, конкатенация и мутация элемента - не нуждаются в нулевом терминаторе. Если вы не пройдете string
функции, ожидающей строку с нулем в конце, она может быть опущена.
Это позволило бы реализации иметь подстроки совместно использовать фактические строковые данные: string::substr
может внутренне содержать ссылку на совместно используемые строковые данные и начальный / конечный диапазон, избегая копирования (и дополнительного выделения) фактических строковых данных. Реализация будет откладывать копирование до тех пор, пока вы не вызовете c_str или не измените любую из строк. Копия никогда не будет сделана, если только стрингсы будут прочитаны.
(Реализация копирования при записи не многим интересна в многопоточных средах, плюс типичная экономия памяти / выделения сегодня не стоит более сложного кода, поэтому это делается редко).
Так же, string::data
позволяет другое внутреннее представление, например, веревка (связанный список сегментов строки). Это может значительно улучшить операции вставки / замены. опять же, список сегментов должен быть свернут в один сегмент при вызове c_str
или же data
,
Цитата из ANSI ISO IEC 14882 2003
(Стандарт C++03):
21.3.6 basic_string string operations [lib.string.ops]
const charT* c_str() const;
Returns: A pointer to the initial element of an array of length size() + 1 whose first size() elements
equal the corresponding elements of the string controlled by *this and whose last element is a
null character specified by charT().
Requires: The program shall not alter any of the values stored in the array. Nor shall the program treat the
returned value as a valid pointer value after any subsequent call to a non-const member function of the
class basic_string that designates the same object as this.
const charT* data() const;
Returns: If size() is nonzero, the member returns a pointer to the initial element of an array whose first
size() elements equal the corresponding elements of the string controlled by *this. If size() is
zero, the member returns a non-null pointer that is copyable and can have zero added to it.
Requires: The program shall not alter any of the values stored in the character array. Nor shall the program
treat the returned value as a valid pointer value after any subsequent call to a non- const member
function of basic_string that designates the same object as this.