Строка c_str() vs. data()
Я прочитал несколько мест, что разница между c_str()
и data()
(в STL и других реализациях) заключается в том, что c_str()
всегда завершается нулем, а data()
- нет.
Насколько я видел в реальных реализациях, они либо выполняют одинаковые, либо data()
вызовы c_str()
.
Что мне здесь не хватает?
Какой из них правильнее использовать в каких сценариях?
Ответы
Ответ 1
Документация верна. Используйте c_str()
, если вам нужна строка с нулевым завершением.
Если разработчикам удалось реализовать data()
в терминах c_str()
, вам не нужно беспокоиться, по-прежнему используйте data()
если вам не нужна строка с нулевым завершением, в некоторой реализации она может оказаться лучше, чем c_str().
Строки необязательно должны состоять из символьных данных, они могут быть составлены с элементами любого типа. В этих случаях data()
более значим. c_str()
, на мой взгляд, очень полезно, когда элементы вашей строки основаны на символах.
Дополнительно: в С++ 11 обе функции должны быть одинаковыми. то есть data
теперь требуется, чтобы оно было завершено нулем. Согласно cppreference: "Возвращенный массив имеет нулевое завершение, то есть данные() и c_str() выполняют одну и ту же функцию".
Ответ 2
В С++ 11/С++ 0x, data()
и c_str()
больше не отличается. И, следовательно, data()
требуется иметь нулевое завершение в конце.
21.4.7.1 basic_string
accessors [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();
ссылочное значение не должно быть изменено.
Ответ 3
Даже знаете, что вы видели, что они делают то же самое, или что .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. Не потому, что в этих случаях dangereous использовать .c_str(), а потому, что более явным является то, что вы работаете с двоичными данными для других, просматривающих ваш код.
Возможная ошибка с использованием .data()
Следующий код неверен и может вызвать segfault в вашей программе:
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 вы всегда можете использовать только один внутренний буфер.
Ответ 4
Уже был дан ответ, некоторые замечания о цели: Свобода реализации.
Операции std::string
- например. итерация, конкатенация и мутация элемента - не нуждаются в нулевом терминаторе. Если вы не передадите string
функции, ожидающей строку с нулевым завершением, ее можно опустить.
Это позволило бы реализации иметь подстроки, разделяющие фактические строковые данные: string::substr
может внутренне содержать ссылку на общие строковые данные и диапазон начала/конца, избегая копирования (и дополнительного выделения) фактических строковых данных, Реализация будет отложить копию до тех пор, пока вы не назовете c_str или не измените ни одну из строк. Никакая копия никогда не будет сделана, если только стримы будут прочитаны.
(реализация копирования на запись не очень приятна в многопоточных средах, плюс типичная экономия памяти/распределения не стоит сегодня более сложного кода, поэтому она редко выполняется).
Аналогично, string::data
допускает другое внутреннее представление, например. веревка (связанный список сегментов строк). Это может значительно улучшить операции вставки/замены. опять же, список сегментов должен быть свернут на один сегмент, когда вы вызываете c_str
или data
.
Ответ 5
Цитата из ANSI ISO IEC 14882 2003
(стандарт С++ 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.
Ответ 6
Все предыдущие комментарии являются консистенцией, но я также хотел бы добавить, что начиная с c ++ 17, str.data() возвращает char * вместо const char *