string c_str () ile data ()


102

Birkaç yer okudum ki, c_str()ve data()(STL ve diğer uygulamalardaki) arasındaki fark c_str()her zaman null sonlandırılırken data()değil. Gerçek uygulamalarda gördüğüm kadarıyla ya aynı şeyi yapıyorlar ya da data()çağırıyorlar c_str().

Burada neyi özlüyorum? Hangi senaryolarda hangisi daha doğrudur?

Yanıtlar:


106

Belgeler doğru. c_str()Boş sonlandırılmış bir dizge istiyorsanız kullanın .

Uygulayıcılar uygulamaya happend Eğer data()açısından c_str()hala kullanmak, endişe gerekmez sana data()sen boş sonlandırılmış olması dize gerekmiyorsa, bazı uygulanmasında bu c_str daha iyi performans ortaya çıkabilir ().

dizelerin karakter verilerinden oluşması gerekmez, herhangi bir türden öğelerle oluşturulabilirler. Bu durumlarda data()daha anlamlıdır. c_str()bence yalnızca dizenizin öğeleri karakter temelli olduğunda gerçekten yararlıdır.

Ekstra : C ++ 11'den itibaren, her iki işlevin de aynı olması gerekir. ie dataartık null sonlandırılması gerekiyor. Cppreference'e göre : "Döndürülen dizi boş sonlandırılmıştır, yani data () ve c_str () aynı işlevi yerine getirir."


4
Ekstra 2: C ++ 17'den itibaren, artık sabit olmayan bir aşırı yük vardır .data(), bu nedenle artık sabit olmayan dizeler için eşdeğer değildirler.
Deduplicator

29

Gelen C ++ 11 / C ++ 0 x , data()ve c_str()artık farklıdır. Ve bu nedenle data()sonunda boş bir sonlandırmaya sahip olmak gerekir.

21.4.7.1 basic_stringerişimciler [string.accessors]

const charT* c_str() const noexcept;

const charT* data() const noexcept;

1 döndürür: Bir işaretçi s, öyle ki p + i == &operator[](i)her biri için ide [0,size()].


21.4.5 temel_dize öğesi erişimi [string.access]

const_reference operator[](size_type pos) const noexcept;

1 Gerektirir: pos <= size (). 2 İade:, *(begin() + pos) if pos < size()aksi takdirde referans değeri charT();olan T tipindeki bir nesneye yapılan referans değiştirilmeyecektir.


Dize, null dahil olmak üzere AFAIK dize verileri için geçerli olan karakter olmayan verilerden oluşuyorsa ne olur?
taz

3
@taz İkili verileri depolarken bile, C ++ 11 bir sonda için std::stringfazladan charbir ayırma gerektirir '\0'. Bunu yaptığınızda std::string s("\0");, hem s.data()[0]ve s.data()[1]0'a değerlendirmek için garanti edilir
bcrist

19

Onların da aynısını yaptıklarını veya .data () 'nın .c_str () çağırdığını gördüğünüzü bilseniz bile, bunun diğer derleyiciler için geçerli olacağını varsaymak doğru değildir. Derleyicinizin gelecekteki bir sürümle değişmesi de mümkündür.

Std :: string kullanmak için 2 neden:

std :: string hem metin hem de rastgele ikili veriler için kullanılabilir.

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

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

Örnek 1 olarak dizenizi kullanırken .c_str () yöntemini kullanmalısınız.

Örnek 2 olarak dizenizi kullanırken .data () yöntemini kullanmalısınız. Bu durumlarda .c_str () kullanmak tehlikeli olduğundan değil, başkalarının gözden geçirmesi için ikili verilerle çalışmanız daha açık olduğu için senin kodun.

.Data () kullanımıyla ilgili olası tuzaklar

Aşağıdaki kod yanlıştır ve programınızda bir segfault'a neden olabilir:

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

Uygulayıcıların .data () ve .c_str () 'yi aynı şeyi yapması neden yaygındır?

Çünkü bunu yapmak daha verimli. .Data () 'nın boş sonlandırılmamış bir şeyi döndürmesini sağlamanın tek yolu, .c_str () veya .data ()' nın dahili tamponlarını kopyalamasına sahip olmak veya sadece 2 tampon kullanmaktır. Tek bir boş sonlandırılmış arabelleğe sahip olmak, her zaman std :: string uygularken yalnızca bir dahili arabellek kullanabileceğiniz anlamına gelir.


6
Aslında, .data () 'nın amacı, dahili arabelleği kopyalamaması gerektiğidir. Bu, bir uygulamanın ihtiyaç duyulana kadar \ 0 üzerinde bir karakter harcamak zorunda olmadığı anlamına gelir. Asla iki tampon istemezsiniz: .c_str () 'yi çağırırsanız, arabelleğe bir \ 0 ekleyin. .data () yine de bu arabelleği döndürebilir.
MSalters

2
Tamamen kabul edildiğinde 2 tampon kullanmak saçma olurdu. .Data'nın bu yüzden tasarlandığını nereden biliyorsunuz?
Brian R. Bondy

@ BrianR.Bondy Bu kodu denedim: .. auto str = string {"Test \ 0String!" }; cout << "VERİ:" << str.data () << endl; Çıktı "Test" ve dizenin tamamı değil. Neyi yanlış yaptım?
programcı

Son kısım yanlış, data ve c_str aynı tamponu 0 ile sonlandırılmadan kullanabilir - c_str ilk çağrıda 0'ı ekleyebilir.
Monica'yı

kafaları kadar, c ++ 11 yapılan .Data () .c_str () için bir takma
hanshenrik

3

Şimdiden yanıtlandı, amaca ilişkin bazı notlar: Uygulama özgürlüğü.

std::stringişlemler - örneğin yineleme, birleştirme ve öğe mutasyonu - sıfır sonlandırıcıya ihtiyaç duymaz. Sıfırla stringsonlandırılmış bir dizge bekleyen bir işleve iletmedikçe, atlanabilir.

Bu, bir uygulamanın alt dizelerin gerçek dizi verilerini paylaşmasına izin verir: string::substrdahili olarak paylaşılan dizi verilerine ve başlangıç ​​/ bitiş aralığına bir referansı tutabilir , gerçek dizi verilerinin kopyasını (ve ek tahsisini) önleyebilir. Uygulama, siz c_str çağırana veya dizelerden herhangi birini değiştirene kadar kopyayı erteler. İlgili strignler sadece okunursa hiçbir kopya yapılmaz.

(yazma üzerine kopyalama uygulaması çok iş parçacıklı ortamlarda pek eğlenceli değildir, ayrıca tipik bellek / ayırma tasarrufları günümüzde daha karmaşık kodlara değmez, bu yüzden nadiren yapılır).


Benzer şekilde, string::datafarklı bir dahili gösterime izin verir, örneğin bir ip (bağlantılı dizi segmentleri listesi). Bu, ekleme / değiştirme işlemlerini önemli ölçüde geliştirebilir. yine, c_strveya aradığınızda segment listesinin tek bir segmente daraltılması gerekir data.


2

Alıntı 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

Önceki tüm komutlar tutarlıdır, ancak c ++ 17'den başlayarak str.data () 'nın const char * yerine bir char * döndürdüğünü de eklemek isterim.


1
Hem consthem de non-constaşırı yükler C ++ 17'den beri mevcuttur.
Gupta
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.