рядок c_str () проти даних ()


102

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

Що я тут пропускаю? Який правильніше використовувати в яких сценаріях?

Відповіді:


105

Документація правильна. Використовуйте, c_str()якщо ви хочете нульовий завершений рядок.

Якщо реалізатори погоджуються реалізувати data()з точки зору, c_str()вам не доведеться турбуватися, все ж використовуйте, data()якщо вам не потрібно, щоб рядок було скасовано на нуль, у деяких реалізація може виявитися краще, ніж c_str ().

рядки не обов'язково повинні складатися з символьних даних, вони можуть складатися з елементами будь-якого типу. У тих випадках data()є більш значущим. c_str()на мою думку, це дуже корисно лише тоді, коли елементи вашої струни базуються на символах.

Додатково : В C ++ 11 і далі обидві функції повинні бути однаковими. тобто dataзараз потрібно припинити дію. Відповідно до cppreference : "Повернений масив закінчується з нульовим завершенням, тобто дані () та 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 доступ до елемента базового рядка [string.access]

const_reference operator[](size_type pos) const noexcept;

1 Потрібно: pos <= size (). 2 Повертає: *(begin() + pos) if pos < size()інакше посилання на об'єкт типу T зі значенням charT();посилається значення не змінюється.


Що робити, якщо рядок складається з даних, що не містять символів, що є законним для даних рядка AFAIK, у тому числі нульовим?
таз

3
@taz Навіть під час зберігання двійкових даних C ++ 11 вимагає std::stringвиділити додатковий charдля останнього '\0'. Коли ви зробите це std::string s("\0");, обидва s.data()[0]і s.data()[1]гарантовано оцінюватимуть до 0.
bcrist

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 ()

Наступний код невірний і може спричинити сегментацію у вашій програмі:

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 буфери. Звідки ти знаєш, що тому .дані були призначені?
Брайан Р. Бонді

@ BrianR.Bondy Я спробував цей код: .. auto str = string {"Test \ 0String!" }; cout << "DATA:" << str.data () << endl; Вихід - "Тест", а не весь рядок. Що я зробив неправильно?
програміст

Остання частина помилкова, дані та c_str можуть використовувати той самий буфер, не припиняючи його 0 - c_str може просто додати 0 під час першого дзвінка.
Згадайте Моніку

heads up, c ++ 11 зроблено .data () псевдонім для .c_str ()
hanshenrik

3

На це вже відповіли, деякі зауваження щодо мети: свобода реалізації.

std::stringоперації - наприклад, ітерація, конкатенація та мутація елементів - не потребують нульового термінатора. Якщо ви не передаєте stringфункцію, яка очікує нульової закінченої рядка, її можна опустити.

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

(реалізація копіювання при записі не є дуже цікавою у багатопотокових середовищах. Крім того, типова економія пам’яті / розподілу не варта сьогодні більш складного коду, тому це рідко робиться).


Аналогічно string::dataдозволяє інше внутрішнє подання, наприклад, мотузка (пов'язаний список сегментів рядків). Це може значно покращити операції вставлення / заміни. знову ж , список сегментів повинен бути згорнутий до одного сегмента , коли ви телефонуєте c_strабо data.


2

Цитата від ANSI ISO IEC 14882 2003(C ++ 03 Standard):

    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.
Гупта
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.