string c_str () vs. data ()

102

Saya telah membaca beberapa tempat bahwa perbedaan antara c_str()dan data()(dalam STL dan implementasi lainnya) adalah yang c_str()selalu dibatalkan sementara data()tidak. Sejauh yang saya lihat dalam implementasi aktual, mereka melakukan hal yang sama atau data()panggilan c_str().

Apa yang kulewatkan di sini? Manakah yang lebih tepat digunakan dalam skenario mana?

Serigala
sumber

Jawaban:

105

Dokumentasinya benar. Gunakan c_str()jika Anda ingin string diakhiri null.

Jika pelaksana yang ingin mengimplementasikan data()dalam hal c_str()Anda tidak perlu khawatir, tetap gunakan data()jika Anda tidak ingin string diakhiri null, dalam beberapa implementasi mungkin berkinerja lebih baik daripada c_str ().

string tidak harus terdiri dari data karakter, string dapat disusun dengan elemen jenis apa pun. Dalam kasus data()tersebut lebih berarti. c_str()menurut saya hanya sangat berguna jika elemen string Anda berbasis karakter.

Ekstra : Dalam C ++ 11 dan seterusnya, kedua fungsi tersebut harus sama. yaitu datasekarang harus diakhiri dengan null. Menurut cppreference : "Array yang dikembalikan adalah null-terminated, yaitu data () dan c_str () melakukan fungsi yang sama."

Scott Langham
sumber
4
Ekstra 2: Dalam C ++ 17 dan seterusnya, sekarang ada juga kelebihan beban non-const untuk .data(), sehingga tidak lagi setara untuk string non-konstan.
Deduplicator
29

Di C ++ 11 / C ++ 0x , data()dan c_str()tidak lagi berbeda. Dan dengan demikian data()diharuskan memiliki penghentian nol di akhir juga.

21.4.7.1 pengakses basic_string[string.accessors]

const charT* c_str() const noexcept;

const charT* data() const noexcept;

1 Returns: Sebuah penunjuk p sedemikian rupa p + i == &operator[](i)untuk setiap iin [0,size()].


21.4.5 akses elemen basic_string [string.access]

const_reference operator[](size_type pos) const noexcept;

1 Membutuhkan: pos <= size (). 2 Returns:, *(begin() + pos) if pos < size()jika tidak referensi ke objek tipe T dengan nilai charT();nilai referensi tidak boleh dimodifikasi.

mfazekas.dll
sumber
Bagaimana jika string terdiri dari data non-karakter, yang legal untuk data string AFAIK, termasuk null?
taz
3
@taz Bahkan saat menyimpan data biner, C ++ 11 memerlukannya std::string pengalokasian ekstra charuntuk trailing '\0'. Jika Anda melakukannya std::string s("\0");, keduanya s.data()[0]dan s.data()[1]dijamin akan bernilai 0.
bcrist
19

Meskipun Anda telah melihat bahwa mereka melakukan hal yang sama, atau bahwa .data () memanggil .c_str (), tidaklah benar untuk mengasumsikan bahwa ini akan menjadi kasus untuk kompiler lain. Mungkin juga kompilator Anda akan berubah dengan rilis mendatang.

2 alasan untuk menggunakan std :: string:

std :: string dapat digunakan untuk teks dan data biner arbitrer.

//Example 1
//Plain text:
std::string s1;
s1 = "abc";

//Example 2
//Arbitrary binary data:
std::string s2;
s2.append("a\0b\0b\0", 6);

Anda harus menggunakan metode .c_str () saat Anda menggunakan string Anda sebagai contoh 1.

Anda harus menggunakan metode .data () saat Anda menggunakan string sebagai contoh 2. Bukan karena berbahaya untuk menggunakan .c_str () dalam kasus ini, tetapi karena lebih eksplisit bahwa Anda bekerja dengan data biner untuk ditinjau orang lain kode Anda.

Kemungkinan jebakan dengan menggunakan .data ()

Kode berikut salah dan dapat menyebabkan segfault dalam program Anda:

std::string s;
s = "abc";   
char sz[512]; 
strcpy(sz, s.data());//This could crash depending on the implementation of .data()

Mengapa umum bagi pelaksana untuk membuat .data () dan .c_str () melakukan hal yang sama?

Karena lebih efisien melakukannya. Satu-satunya cara untuk membuat .data () mengembalikan sesuatu yang tidak diakhiri null, adalah dengan membuat .c_str () atau .data () menyalin buffer internalnya, atau hanya menggunakan 2 buffer. Memiliki satu buffer diakhiri null selalu berarti bahwa Anda selalu dapat menggunakan hanya satu buffer internal saat mengimplementasikan std :: string.

Brian R. Bondy
sumber
6
Sebenarnya, inti dari .data () adalah ia seharusnya tidak menyalin buffer internal. Ini berarti implementasi tidak harus membuang-buang karakter di \ 0 hingga diperlukan. Anda tidak akan pernah menginginkan dua buffer: jika Anda DO memanggil .c_str (), tambahkan \ 0 ke buffer. .data () masih bisa mengembalikan buffer itu.
MSalters
2
Sepenuhnya setuju akan konyol menggunakan 2 buffer. Bagaimana Anda tahu mengapa .data dimaksudkan?
Brian R. Bondy
@ BrianR.Bondy Saya mencoba kode ini: .. auto str = string {"Test \ 0String!" }; cout << "DATA:" << str.data () << endl; Outputnya adalah "Test" dan bukan keseluruhan string, Apa yang salah?
programmer
Bagian terakhir salah, data dan c_str dapat menggunakan buffer yang sama tanpa harus diakhiri 0 - c_str cukup menambahkan 0 pada panggilan pertama.
Ingat Monica
kepala, c ++ 11 membuat .data () alias untuk .c_str ()
hanshenrik
3

Itu sudah dijawab, beberapa catatan tentang tujuan: Kebebasan implementasi.

std::stringoperasi - misalnya iterasi, penggabungan, dan mutasi elemen - tidak memerlukan terminator nol. Kecuali Anda meneruskan stringke fungsi yang mengharapkan string diakhiri nol, itu bisa dihilangkan.

Ini akan memungkinkan implementasi memiliki substring yang berbagi data string aktual: secara string::substrinternal dapat menyimpan referensi ke data string bersama, dan rentang awal / akhir, menghindari salinan (dan alokasi tambahan) dari data string aktual. Implementasinya akan menunda salinan sampai Anda memanggil c_str atau mengubah salah satu string. Salinan tidak akan pernah dibuat jika strign yang terlibat baru saja dibaca.

(implementasi copy-on-write tidak terlalu menyenangkan di lingkungan multithread, ditambah penghematan alokasi / memori yang khas tidak sebanding dengan kode yang lebih kompleks saat ini, jadi jarang dilakukan).


Demikian pula, string::datamemungkinkan representasi internal yang berbeda, misalnya tali (daftar segmen tali yang terhubung). Ini dapat meningkatkan operasi penyisipan / penggantian secara signifikan. sekali lagi, daftar segmen harus diciutkan menjadi satu segmen saat Anda memanggil c_stratau data.

peterchen
sumber
2

Kutipan dari 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.
Mihran Hovsepyan
sumber
2

Semua perintah sebelumnya adalah konsistensi, tetapi saya juga ingin menambahkan bahwa mulai c ++ 17, str.data () mengembalikan sebuah char * daripada const char *

Nam Vu
sumber
1
Keduanya constdan non-constkelebihan beban tersedia sejak C ++ 17.
Gupta