Строка 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 *