Diberikan katakan ...
std::string x = "hello";
Mendapatkan `char *` atau `const char *` dari `string`
Cara mendapatkan penunjuk karakter yang valid sambil x
tetap dalam cakupan dan tidak dimodifikasi lebih lanjut
C ++ 11 menyederhanakan banyak hal; semua yang berikut memberikan akses ke buffer string internal yang sama:
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...
Semua petunjuk di atas akan memiliki nilai yang sama - alamat karakter pertama di buffer. Bahkan string kosong memiliki "karakter pertama dalam buffer", karena C ++ 11 menjamin untuk selalu menyimpan karakter terminator NUL / 0 tambahan setelah konten string yang ditetapkan secara eksplisit (mis. std::string("this\0that", 9)
Akan memiliki holding buffer "this\0that\0"
).
Diberikan salah satu petunjuk di atas:
char c = p[n]; // valid for n <= x.size()
// i.e. you can safely read the NUL at p[x.size()]
Hanya untuk yang bukan const
penunjuk p_writable_data
dan dari &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
Menulis NUL di tempat lain dalam string tidak mengubah string
's size()
; string
Diijinkan mengandung sejumlah NUL - mereka tidak diberi perlakuan khusus oleh std::string
(sama dalam C ++ 03).
Di C ++ 03 , hal-hal yang jauh lebih rumit (perbedaan utama disorot ):
x.data()
- kembali
const char*
ke buffer internal string yang tidak diperlukan oleh Standar untuk menyimpulkan dengan NUL (yaitu mungkin ['h', 'e', 'l', 'l', 'o']
diikuti oleh nilai-nilai tidak diinisialisasi atau sampah, dengan akses tidak disengaja yang memiliki perilaku yang tidak ditentukan ).
x.size()
karakter aman untuk dibaca, yaitu x[0]
melaluix[x.size() - 1]
- untuk string kosong, Anda dijamin beberapa pointer NULL yang 0 dapat ditambahkan dengan aman (hore!), tetapi Anda tidak boleh dereferensi pointer itu.
&x[0]
- untuk string kosong ini memiliki perilaku yang tidak jelas (21.3.4)
- mis. diberikan
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
Anda tidak harus menelepon f(&x[0], x.size());
kapan x.empty()
- gunakan saja f(x.data(), ...)
.
- jika tidak, sesuai
x.data()
tetapi:
- untuk non-
const
x
ini menghasilkan non- const
char*
pointer; Anda dapat menimpa konten string
x.c_str()
- kembali
const char*
ke representasi ASCIIZ (diakhiri NUL) dari nilai (yaitu ['h', 'e', 'l', 'l', 'o', '\ 0']).
- meskipun sedikit jika ada implementasi yang memilih untuk melakukannya, Standar C ++ 03 diucapkan untuk memungkinkan implementasi string kebebasan untuk membuat buffer NUL-dihentikan dengan cepat , dari buffer yang berpotensi non-NUL yang diakhiri oleh "terkena" oleh
x.data()
dan&x[0]
x.size()
+1 karakter aman untuk dibaca.
- dijamin aman bahkan untuk string kosong (['\ 0']).
Konsekuensi mengakses indeks hukum luar
Dengan cara apa pun Anda mendapatkan pointer, Anda tidak boleh mengakses memori lebih jauh dari pointer daripada karakter dijamin hadir dalam deskripsi di atas. Upaya untuk melakukannya memiliki perilaku yang tidak terdefinisi , dengan kemungkinan crash aplikasi dan hasil sampah yang sangat nyata bahkan untuk dibaca, dan juga data grosir, tumpukan korupsi dan / atau kerentanan keamanan untuk penulisan.
Kapan pointer tersebut dibatalkan?
Jika Anda memanggil beberapa string
fungsi anggota yang memodifikasi string
atau cadangan kapasitas lebih lanjut, nilai pointer apa pun yang dikembalikan sebelumnya dengan salah satu metode di atas tidak valid . Anda dapat menggunakan metode itu lagi untuk mendapatkan pointer lain. (Aturannya sama dengan iterator ke string
s).
Lihat juga Cara mendapatkan penunjuk karakter yang valid bahkan setelah x
meninggalkan cakupan atau dimodifikasi lebih lanjut di bawah ....
Jadi, mana yang lebih baik untuk digunakan?
Dari C ++ 11, gunakan .c_str()
untuk data ASCIIZ, dan .data()
untuk data "biner" (dijelaskan lebih lanjut di bawah).
Dalam C ++ 03, penggunaan .c_str()
kecuali tertentu yang .data()
memadai, dan lebih memilih .data()
lebih &x[0]
seperti itu aman untuk string kosong ....
... cobalah untuk memahami program yang cukup untuk digunakan data()
saat yang tepat, atau Anda mungkin akan membuat kesalahan lain ...
Karakter ASCII NUL '\ 0' yang dijamin oleh .c_str()
digunakan oleh banyak fungsi sebagai nilai sentinel yang menunjukkan akhir data yang relevan dan aman untuk diakses. Ini berlaku untuk C ++ - hanya fungsi seperti fungsi say fstream::fstream(const char* filename, ...)
dan shared-with-C seperti strchr()
, dan printf()
.
Mengingat .c_str()
jaminan C ++ 03 tentang buffer yang dikembalikan adalah set super .data()
, Anda selalu dapat menggunakan dengan aman .c_str()
, tetapi orang terkadang tidak melakukannya karena:
- menggunakan
.data()
komunikasi dengan pemrogram lain yang membaca kode sumber bahwa data tersebut bukan ASCIIZ (melainkan, Anda menggunakan string untuk menyimpan blok data (yang kadang-kadang bahkan tidak benar-benar tekstual)), atau Anda meneruskannya ke fungsi lain yang memperlakukannya sebagai blok data "biner". Ini bisa menjadi wawasan penting dalam memastikan bahwa perubahan kode programmer lain terus menangani data dengan benar.
- Hanya C ++ 03: ada sedikit peluang bahwa
string
implementasi Anda perlu melakukan alokasi memori tambahan dan / atau menyalin data untuk menyiapkan buffer NUL yang dihentikan
Sebagai petunjuk lebih lanjut, jika parameter fungsi memerlukan ( const
) char*
tetapi tidak bersikeras untuk mendapatkannya x.size()
, fungsi tersebut mungkin membutuhkan input ASCIIZ, jadi .c_str()
ini adalah pilihan yang baik (fungsi perlu tahu di mana teks berakhir, entah bagaimana, jadi jika tidak parameter yang terpisah hanya dapat berupa konvensi seperti awalan panjang atau sentinel atau panjang yang diharapkan tetap).
Cara mendapatkan penunjuk karakter yang valid bahkan setelah x
meninggalkan ruang lingkup atau dimodifikasi lebih lanjut
Anda harus menyalin konten string
x
ke area memori baru di luar x
. Buffer eksternal ini dapat berada di banyak tempat seperti string
variabel array karakter atau lainnya , mungkin atau mungkin tidak memiliki masa hidup yang berbeda daripada x
karena berada dalam ruang lingkup yang berbeda (mis. Namespace, global, statis, tumpukan, memori bersama, memori yang dipetakan file) .
Untuk menyalin teks dari std::string x
ke dalam array karakter independen:
// 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);
Alasan lain untuk menginginkan char*
atau const char*
dihasilkan daristring
Jadi, di atas Anda telah melihat cara mendapatkan ( const
) char*
, dan cara membuat salinan teks yang independen dari yang asli string
, tetapi apa yang dapat Anda lakukan dengannya? Sebuah contoh acak ...
- beri "C" akses kode ke
string
teks C ++ , seperti padaprintf("x is '%s'", x.c_str());
- menyalin
x
teks ke buffer yang ditentukan oleh pemanggil fungsi Anda (mis. strncpy(callers_buffer, callers_buffer_size, x.c_str())
), atau memori yang mudah menguap digunakan untuk I / O perangkat (mis. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;
)
- tambahkan
x
teks ke array karakter yang sudah mengandung beberapa teks ASCIIZ (mis. strcat(other_buffer, x.c_str())
) - hati-hati jangan sampai menyerbu buffer (dalam banyak situasi Anda mungkin perlu menggunakan strncat
)
- mengembalikan sebuah
const char*
atau char*
dari suatu fungsi (mungkin karena alasan historis - klien menggunakan API Anda yang ada - atau untuk kompatibilitas C Anda tidak ingin mengembalikan std::string
, tetapi ingin menyalin string
data Anda di suatu tempat untuk penelepon)
- hati-hati untuk tidak mengembalikan pointer yang mungkin ditereferensi oleh pemanggil setelah
string
variabel lokal yang menunjuk pointer telah meninggalkan ruang lingkup
- beberapa proyek dengan objek bersama dikompilasi / ditautkan untuk
std::string
implementasi yang berbeda (mis. STLport dan compiler-native) dapat meneruskan data sebagai ASCIIZ untuk menghindari konflik