строка c_str () vs. data ()

102

Я читал несколько мест, где разница между c_str()и data()(в STL и других реализациях) заключается в том, что c_str()всегда завершается нулем, а data()нет. Насколько я видел в реальных реализациях, они либо делают то же самое, либо data()вызывают c_str().

Что мне здесь не хватает? Какой из них правильнее использовать в каких сценариях?

Волк
источник

Ответы:

106

Документация верна. Используйте, c_str()если вам нужна строка с завершающим нулем.

Если разработчики реализовали с data()точки зрения того, что c_str()вам не о чем беспокоиться, все равно используйте, data()если вам не нужно, чтобы строка заканчивалась нулем, в некоторых реализациях это может оказаться лучше, чем c_str ().

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

Дополнительно : начиная с C ++ 11 обе функции должны быть одинаковыми. т.е. dataтеперь требуется, чтобы он завершался нулем. Согласно cppreference : «Возвращаемый массив заканчивается нулем, то есть data () и c_str () выполняют одну и ту же функцию».

Скотт Лэнгэм
источник
4
Дополнительно 2: в C ++ 17 и последующих версиях теперь также есть неконстантная перегрузка для .data(), поэтому они больше не эквивалентны для неконстантных строк.
Дедупликатор
29

В 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();на которое ссылается значение, не должна изменяться.

Мфазекас
источник
Что, если строка состоит из несимвольных данных, что допустимо для строковых данных AFAIK, включая null?
taz
3
@taz Даже при хранении двоичных данных C ++ 11 требует std::stringвыделения дополнительного места charдля трейлинга '\0'. Когда вы это сделаете std::string s("\0");, оба s.data()[0]и s.data()[1]гарантированно
дадут
19

Даже зная, что вы видели, что они делают то же самое или что .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 ()

Следующий код неверен и может вызвать 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.

Брайан Р. Бонди
источник
6
На самом деле смысл .data () в том, что он не должен копировать внутренний буфер. Это означает, что реализации не нужно тратить символ на \ 0, пока он не понадобится. Вам никогда не понадобятся два буфера: если вы ДЕЙСТВИТЕЛЬНО вызываете .c_str (), добавьте \ 0 в буфер. .data () все еще может возвращать этот буфер.
MSalters
2
Полностью согласен, использовать 2 буфера было бы смешно. Но откуда вы знаете, почему был предназначен .data?
Брайан Р. Бонди,
@ BrianR.Bondy Я пробовал этот код: .. auto str = string {"Test \ 0String!" }; cout << "DATA:" << str.data () << endl; Результатом будет «Тест», а не вся строка. Что я сделал не так?
программист
Последняя часть неверна, данные и c_str могут использовать один и тот же буфер без завершения 0 - c_str может просто добавить 0 при первом вызове.
Помните Монику
хедз-ап, c ++ 11 сделал .data () псевдонимом для .c_str ()
hanshenrik 02
3

На него уже был дан ответ, несколько примечаний о цели: Свобода реализации.

std::stringоперации - например, итерация, конкатенация и изменение элементов - не нуждаются в нулевом ограничителе. Если вы не передадите в stringфункцию, ожидающую строку с нулевым завершением, его можно опустить.

Это позволило бы реализации иметь подстроки, совместно использующие фактические строковые данные: string::substrмогло бы внутренне содержать ссылку на общие строковые данные и начальный / конечный диапазон, избегая копирования (и дополнительного выделения) фактических строковых данных. Реализация отложит копирование до тех пор, пока вы не вызовете c_str или не измените какую-либо из строк. Никакая копия не будет сделана, если будут просто прочитаны задействованные строки.

(реализация копирования при записи не очень интересна в многопоточных средах, плюс типичная экономия памяти / распределения сегодня не стоит более сложного кода, поэтому это делается редко).


Точно так же string::dataдопускается другое внутреннее представление, например веревка (связанный список сегментов строки). Это может значительно улучшить операции вставки / замены. опять же, список сегментов должен быть свернут до одного сегмента, когда вы вызываете c_strили data.

Петерхен
источник
2

Цитата из 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.
Мигран Овсепян
источник
2

Все предыдущие комментарии являются согласованными, но я также хотел бы добавить, что начиная с C ++ 17 str.data () возвращает char * вместо const char *

Нам Ву
источник
1
Оба constи non-constперегрузки доступны с C ++ 17.
Гупта