Diyelim ki ...
std::string x = "hello";
Bir dizeden `char *` ya da 'const char *' alma
x
Kapsamda kalmaya devam eden ve daha fazla değiştirilmeyen bir karakter işaretçisi nasıl edinilir
C ++ 11 işleri kolaylaştırır; aşağıdakilerin tümü aynı dahili dize arabelleğine erişim sağlar:
const char* p_c_str = x.c_str();
const char* p_data = x.data();
char* p_writable_data = x.data(); // for non-const x from C++17
const char* p_x0 = &x[0];
char* p_x0_rw = &x[0]; // compiles iff x is not const...
Yukarıdaki tüm işaretçiler aynı değeri tutacaktır - tampondaki ilk karakterin adresi. Boş bir dize bile "arabellekteki ilk karakter" e sahiptir, çünkü C ++ 11, açıkça atanan dize içeriğinden sonra her zaman fazladan bir NUL / 0 sonlandırıcı karakteri bulundurmayı garanti eder (örneğin std::string("this\0that", 9)
, bir tampon tutma özelliğine sahip olacaktır "this\0that\0"
).
Yukarıdaki işaretçilerden herhangi biri göz önüne alındığında:
char c = p[n]; // valid for n <= x.size()
// i.e. you can safely read the NUL at p[x.size()]
Sadece olmayan için const
işaretçi p_writable_data
ve arasından &x[0]
:
p_writable_data[n] = c;
p_x0_rw[n] = c; // valid for n <= x.size() - 1
// i.e. don't overwrite the implementation maintained NUL
Dizede başka bir NUL yazma yok değil değiştirmek string
'ın size()
; string
herhangi bir sayıda NUL içermesine izin verilir - onlara özel bir tedavi verilmez std::string
(C ++ 03 ile aynı).
In C ++ 03 , işler çok daha (önemli farklılıklar komplike edildi vurgulanan ):
x.data()
- Standart tarafından bir NUL ile sonuçlanması gerekmeyen
const char*
dizenin dahili arabelleğine döner (örneğin , tanımlanmamış davranışa sahip yanlışlıkla erişimleri olan, başlatılmamış veya çöp değerleri gelebilir ).
['h', 'e', 'l', 'l', 'o']
x.size()
karakterleri okumak için güvenlidir, yani x[0]
içindenx[x.size() - 1]
- boş dizeler için, 0'ın güvenli bir şekilde eklenebileceği (yaşasın!) NULL olmayan bir işaretçi garantilidir, ancak bu işaretçiyi kullanmamanız gerekir.
&x[0]
- boş dizeler için bunun tanımlanmamış davranışı vardır (21.3.4)
- örneğin verilen
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
Eğer deme f(&x[0], x.size());
zaman x.empty()
sadece kullanmak - f(x.data(), ...)
.
- aksi halde,
x.data()
ancak:
- bunun için işaretçi
const
x
olmayan bir sonuç verir const
char*
; dize içeriğinin üzerine yazabilirsiniz
x.c_str()
const char*
değerin ASCIIZ (NUL sonlu) gösterimine geri döner (yani ['h', 'e', 'l', 'l', 'o', '\ 0']).
- herhangi uygulamalar bunu seçerseniz kaç, C ++ 03 Standart bir oluşturmak için String Uygulama özgürlüğüne izin ifadeli rağmen ayrı boş karakter sonlandırmalı tampon anında potansiyel olmayan NUL dan, tarafından "maruz" tampon sonlandırıldı
x.data()
ve&x[0]
x.size()
+ 1 karakteri güvenle okuyabilirsiniz.
- boş dizeler için bile güvenli (['\ 0']).
Yasal endekslerin dışına çıkmanın sonuçları
İşaretçiyi nereden alırsanız alın, yukarıdaki açıklamalarda bulunması garanti edilen karakterlerden daha fazla şekilde, işaretçiden itibaren belleğe erişmemeniz gerekir. Bunu yapmaya yönelik girişimler , okumalar için bile uygulama çökmeleri ve çöp sonuçlarının gerçek bir şansı ve ek olarak toptan veriler, yığın bozulması ve / veya yazma için güvenlik açıkları olan tanımsız davranışlara sahiptir .
Bu işaretçiler ne zaman geçersiz olur?
Daha fazla kapasiteyi string
değiştiren string
veya daha fazla kapasite ayıran bazı üye işlevlerini çağırırsanız , yukarıdaki yöntemlerden herhangi biri tarafından önceden döndürülen işaretçi değerleri geçersiz kılınır . Başka bir işaretçi almak için bu yöntemleri tekrar kullanabilirsiniz. (Kurallar, yineleyiciler için olanlarla aynıdır string
).
Ayrıca , bir karakter işaretçisi x
kapsamı terk ettikten sonra da geçerli kılmak veya aşağıda daha fazla değişiklik yapmak için bkz.
Peki hangisi daha iyi ?
C ++ 11'den .c_str()
ASCIIZ verileri ve .data()
"ikili" veriler (aşağıda daha ayrıntılı olarak açıklanmıştır) için kullanın.
C ++ 03, kullanım .c_str()
belli olmadıkça .data()
yeterli olduğunu ve tercih .data()
üzerinde &x[0]
boş dizeleri için güvenli olarak ....
... programı data()
uygun olduğunda kullanmak için yeterince anlamaya çalışın , aksi takdirde muhtemelen başka hatalar yaparsınız ...
ASCII NUL '\ 0' karakteri .c_str()
, birçok işlev tarafından, ilgili ve erişilmesi güvenli verilerin sonunu gösteren bir sentinel değeri olarak kullanılır. Bu hem C ++ için geçerlidir - yalnızca say fstream::fstream(const char* filename, ...)
gibi işlevler strchr()
ve, ve gibi C ile paylaşılan işlevler printf()
.
C ++ 03'ün .c_str()
döndürülen arabellekle ilgili garantileri göz önüne alındığında .data()
, her zaman güvenle kullanabilirsiniz .c_str()
, ancak insanlar bazen şunları yapmaz:
- kullanarak
.data()
, kaynak kodu okuyan diğer programcılarla iletişim kurarak verilerin ASCIIZ olmadığını (bunun yerine, bir veri bloğunu saklamak için dizeyi kullanıyorsunuz (bazen gerçekte metinsel bile değildir) veya "ikili" veri bloğu olarak değerlendiren başka bir işlev. Bu, diğer programcıların kod değişikliklerinin verileri düzgün işlemeye devam etmesini sağlama konusunda önemli bir fikir olabilir.
- Yalnızca C ++ 03:
string
NUL sonlandırılmış arabelleği hazırlamak için uygulamanızın birazdan fazladan bellek ayırma ve / veya veri kopyalama yapması gerekecektir.
Başka bir ipucu olarak, bir işlevin parametreleri ( const
) gerektiriyor char*
ancak alma konusunda ısrar etmiyorsa x.size()
, işlev muhtemelen bir ASCIIZ girişine ihtiyaç duyar, bu nedenle .c_str()
iyi bir seçimdir (işlev, metnin bir şekilde nerede sona erdiğini bilmelidir, bu yüzden değilse ayrı bir parametre yalnızca uzunluk öneki veya sentinel veya sabit bir beklenen uzunluk gibi bir kural olabilir).
Bir karakter işaretçisi x
kapsamı terk ettikten veya daha sonra değiştirildikten sonra bile geçerli hale getirme
Öğesinin içeriğini dışarıdaki yeni bir bellek alanına kopyalamanız gerekir . Bu harici arabellek, başka bir karakter veya karakter dizisi değişkeni gibi birçok yerde olabilir, farklı bir kapsamda olmasından (örneğin ad alanı, genel, statik, yığın, paylaşılan bellek, bellek eşlenmiş dosya) farklı bir ömre sahip olabilir veya olmayabilir. .string
x
x
string
x
Metni std::string x
bağımsız bir karakter dizisine kopyalamak için :
// USING ANOTHER STRING - AUTO MEMORY MANAGEMENT, EXCEPTION SAFE
std::string old_x = x;
// - old_x will not be affected by subsequent modifications to x...
// - you can use `&old_x[0]` to get a writable char* to old_x's textual content
// - you can use resize() to reduce/expand the string
// - resizing isn't possible from within a function passed only the char* address
std::string old_x = x.c_str(); // old_x will terminate early if x embeds NUL
// Copies ASCIIZ data but could be less efficient as it needs to scan memory to
// find the NUL terminator indicating string length before allocating that amount
// of memory to copy into, or more efficient if it ends up allocating/copying a
// lot less content.
// Example, x == "ab\0cd" -> old_x == "ab".
// USING A VECTOR OF CHAR - AUTO, EXCEPTION SAFE, HINTS AT BINARY CONTENT, GUARANTEED CONTIGUOUS EVEN IN C++03
std::vector<char> old_x(x.data(), x.data() + x.size()); // without the NUL
std::vector<char> old_x(x.c_str(), x.c_str() + x.size() + 1); // with the NUL
// USING STACK WHERE MAXIMUM SIZE OF x IS KNOWN TO BE COMPILE-TIME CONSTANT "N"
// (a bit dangerous, as "known" things are sometimes wrong and often become wrong)
char y[N + 1];
strcpy(y, x.c_str());
// USING STACK WHERE UNEXPECTEDLY LONG x IS TRUNCATED (e.g. Hello\0->Hel\0)
char y[N + 1];
strncpy(y, x.c_str(), N); // copy at most N, zero-padding if shorter
y[N] = '\0'; // ensure NUL terminated
// USING THE STACK TO HANDLE x OF UNKNOWN (BUT SANE) LENGTH
char* y = alloca(x.size() + 1);
strcpy(y, x.c_str());
// USING THE STACK TO HANDLE x OF UNKNOWN LENGTH (NON-STANDARD GCC EXTENSION)
char y[x.size() + 1];
strcpy(y, x.c_str());
// USING new/delete HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = new char[x.size() + 1];
strcpy(y, x.c_str());
// or as a one-liner: char* y = strcpy(new char[x.size() + 1], x.c_str());
// use y...
delete[] y; // make sure no break, return, throw or branching bypasses this
// USING new/delete HEAP MEMORY, SMART POINTER DEALLOCATION, EXCEPTION SAFE
// see boost shared_array usage in Johannes Schaub's answer
// USING malloc/free HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = strdup(x.c_str());
// use y...
free(y);
Diğer nedenler istemek için char*
ya const char*
a oluşturulanstring
Gördüğünüz yukarıdaki Peki, nasıl bir (olsun const
) char*
, ve nasıl orijinal metin bağımsız bir kopyasını yapmak için string
, ama ne olabilir yapmak onunla? Örneklerin rastgele dağılması ...
- olduğu gibi C ++
string
metnine "C" kodu erişimi verinprintf("x is '%s'", x.c_str());
x
metnini işlevinizin arayanı tarafından belirtilen bir ara belleğe kopyalayın (örn. strncpy(callers_buffer, callers_buffer_size, x.c_str())
) veya cihaz G / Ç'si için kullanılan geçici belleğe (ör. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;
)
- Append
x
zaten bazı ASCIIZ metni (örneğin içeren bir karakter dizisine 'in metin strcat(other_buffer, x.c_str())
tampon taşması dikkat edin (birçok durumda size kullanım gerekebilir -) strncat
)
- bir işlevden
const char*
veya char*
bir işlevden döndürme (belki geçmiş nedenlerden ötürü - müşteri mevcut API'nızı kullanıyorsa - veya C uyumluluğu için geri dönmek istemiyorsunuz std::string
, ancak string
arayan kişinin verilerini bir yere kopyalamak istiyorsunuz )
string
İşaretçinin işaret ettiği yerel bir değişkenin kapsam dışına çıkmasından sonra arayan tarafından geri çevrilebilecek bir işaretçiyi döndürmemeye dikkat edin
- farklı
std::string
uygulamalar için derlenmiş / bağlantılı (örneğin, STLport ve derleyici-yerel) paylaşılan nesnelerin bulunduğu bazı projeler çakışmaları önlemek için verileri ASCIIZ olarak geçirebilir