ให้พูด ...
std::string x = "hello";
รับ `char * 'หรือ` const char *' จาก 'string'
วิธีรับพอยน์เตอร์พอยน์เตอร์ที่ใช้ได้ในขณะที่xยังอยู่ในขอบเขตและไม่ได้รับการแก้ไขเพิ่มเติม
C ++ 11ลดความซับซ้อนของสิ่งต่าง ๆ ; ทั้งหมดต่อไปนี้ให้การเข้าถึงบัฟเฟอร์สตริงภายในเดียวกัน:
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...
ตัวชี้ข้างต้นทั้งหมดจะเก็บค่าเดียวกัน - ที่อยู่ของตัวละครตัวแรกในบัฟเฟอร์ แม้แต่สตริงว่างยังมี "อักขระตัวแรกในบัฟเฟอร์" เพราะ C ++ 11 รับประกันว่าจะเก็บอักขระตัวคั่น NUL / 0 เพิ่มเติมเสมอหลังจากเนื้อหาสตริงที่กำหนดไว้อย่างชัดเจน (เช่นstd::string("this\0that", 9)จะมีการพักบัฟเฟอร์"this\0that\0")
รับตัวชี้ใด ๆ ข้างต้น:
char c = p[n]; // valid for n <= x.size()
// i.e. you can safely read the NUL at p[x.size()]
สำหรับconstตัวชี้p_writable_dataและจาก&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
การเขียน NUL ที่อื่นในสตริงจะไม่เปลี่ยนstring's size(); stringอนุญาตให้มีจำนวน NUL ใด ๆ - พวกเขาไม่ได้รับการดูแลเป็นพิเศษโดยstd::string(เหมือนกันใน C ++ 03)
ในC ++ 03สิ่งต่าง ๆ มีความซับซ้อนมากขึ้น ( เน้นความแตกต่างที่สำคัญ ):
x.data()
- กลับ
const char*ไปที่บัฟเฟอร์ภายในของสตริงซึ่งมาตรฐานไม่ต้องการเพื่อสรุปด้วย NUL (เช่นอาจ['h', 'e', 'l', 'l', 'o']ตามด้วยค่าเริ่มต้นหรือค่าขยะโดยไม่ได้ตั้งใจเข้าถึงโดยที่พฤติกรรมไม่ได้กำหนด )
x.size()อักขระมีความปลอดภัยในการอ่านเช่นx[0]ผ่านx[x.size() - 1]
- สำหรับสตริงว่างคุณรับประกันตัวชี้ที่ไม่ใช่ค่า NULL ซึ่งสามารถเพิ่ม 0 ได้อย่างปลอดภัย (hurray!) แต่คุณไม่ควรอ้างถึงตัวชี้นั้น
&x[0]
- สำหรับสตริงว่างนี้มีพฤติกรรมที่ไม่ได้กำหนด (21.3.4)
- เช่นให้
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }คุณไม่ต้องโทรหาf(&x[0], x.size());เมื่อx.empty()- f(x.data(), ...)เพียงแค่ใช้
- มิฉะนั้นเป็นต่อ
x.data()แต่:
- สำหรับสิ่งที่ไม่ได้
const xผลตอบแทนจะไม่ใช่const char*ตัวชี้; คุณสามารถเขียนทับเนื้อหาสตริงได้
x.c_str()
- กลับ
const char*สู่การแทน ASCIIZ (ยกเลิกด้วย NUL) ของค่า (เช่น ['h', 'e', 'l', 'l', 'o', '\ 0'])
- แม้จะไม่กี่ถ้าการใช้งานใด ๆ เลือกที่จะทำเช่นนั้น C ++ 03 มาตรฐานได้รับการคำเพื่อให้การดำเนินการสตริงอิสระในการสร้างบัฟเฟอร์ NUL สิ้นสุดที่แตกต่างกัน ในการบินจากที่อาจเกิดขึ้นไม่ใช่ NUL สิ้นสุดบัฟเฟอร์ "สัมผัส" โดย
x.data()และ&x[0]
x.size() +1 ตัวอักษรมีความปลอดภัยในการอ่าน
- รับประกันความปลอดภัยแม้สำหรับสตริงว่าง (['\ 0'])
ผลที่ตามมาของการเข้าถึงดัชนีนอกกฎหมาย
ไม่ว่าคุณจะได้รับพอยน์เตอร์แบบใดคุณจะต้องไม่เข้าถึงหน่วยความจำเพิ่มเติมจากพอยน์เตอร์กว่าตัวละครที่รับประกันว่าจะมีอยู่ในคำอธิบายข้างต้น ความพยายามที่จะทำเช่นนั้นมีพฤติกรรมที่ไม่ได้กำหนดโดยมีโอกาสที่แท้จริงของการล่มของแอปพลิเคชั่นและผลลัพธ์ขยะแม้กระทั่งสำหรับการอ่านและการส่งข้อมูลเพิ่มเติมความเสียหายจากสแต็กและ / หรือช่องโหว่ด้านความปลอดภัยสำหรับการเขียน
ตัวชี้เหล่านั้นจะได้รับการตรวจสอบเมื่อใด
ถ้าคุณเรียกบางstringฟังก์ชั่นของสมาชิกที่ปรับเปลี่ยนstringหรือสำรองความจุเพิ่มเติมค่าตัวชี้ใด ๆ กลับมาก่อนโดยใด ๆ ของวิธีการดังกล่าวเป็นโมฆะ คุณสามารถใช้วิธีการเหล่านั้นอีกครั้งเพื่อรับตัวชี้อื่น (กฎนั้นเหมือนกันสำหรับตัววนซ้ำในstrings)
ดูเพิ่มเติมวิธีการรับตัวชี้อักขระที่ถูกต้องแม้หลังจากxออกจากขอบเขตหรือแก้ไขเพิ่มเติมด้านล่าง ...
ดังนั้นจะใช้อะไรดีกว่ากัน ?
จาก C ++ 11 ใช้.c_str()สำหรับข้อมูล ASCIIZ และ.data()สำหรับข้อมูล "ไบนารี" (อธิบายเพิ่มเติมด้านล่าง)
ใน C ++ 03 ให้ใช้.c_str()เว้นแต่จะแน่ใจว่า.data()เพียงพอและชอบ.data()มากกว่า&x[0]เพราะปลอดภัยสำหรับสตริงว่าง ....
... พยายามเข้าใจโปรแกรมมากพอที่จะใช้data()เมื่อเหมาะสมหรือคุณอาจทำผิดพลาดอื่น ๆ ...
อักขระ ASCII NUL '\ 0' รับประกันโดย.c_str()ถูกใช้งานโดยฟังก์ชั่นมากมายเป็นค่ารักษาการณ์แสดงถึงจุดสิ้นสุดของข้อมูลที่เกี่ยวข้องและปลอดภัยต่อการเข้าถึง นี้นำไปใช้ทั้ง C ++ - ฟังก์ชั่นเท่านั้นเช่นการพูดfstream::fstream(const char* filename, ...)และฟังก์ชั่นที่ใช้ร่วมกันกับ-C เหมือนและstrchr()printf()
เมื่อพิจารณาจาก C ++ 03 .c_str()เกี่ยวกับบัฟเฟอร์ที่ส่งคืนเป็นชุดสุดยอดของ.data()คุณสามารถใช้งานได้อย่างปลอดภัย.c_str()แต่บางครั้งผู้คนก็ไม่ได้เพราะ:
- การใช้
.data()สื่อสารกับโปรแกรมเมอร์คนอื่น ๆ ที่อ่านซอร์สโค้ดว่าข้อมูลไม่ใช่ ASCIIZ (แต่คุณกำลังใช้สตริงเพื่อจัดเก็บบล็อกข้อมูล (ซึ่งบางครั้งก็ไม่ได้เป็นข้อความจริง ๆ )) หรือว่าคุณผ่านมันไป ฟังก์ชั่นอื่นที่ถือว่าเป็นบล็อกของข้อมูล "ไบนารี" นี่เป็นข้อมูลเชิงลึกที่สำคัญอย่างยิ่งในการรับรองว่าการเปลี่ยนแปลงรหัสของโปรแกรมเมอร์อื่น ๆ จะยังคงจัดการข้อมูลได้อย่างถูกต้อง
- C ++ 03 เท่านั้น: มีโอกาสเล็กน้อยที่
stringการใช้งานของคุณจะต้องทำการจัดสรรหน่วยความจำเพิ่มเติมและ / หรือคัดลอกข้อมูลเพื่อเตรียมบัฟเฟอร์ที่ยกเลิก NUL
หากพารามิเตอร์ของฟังก์ชั่นต้องการ ( const) char*แต่ไม่ยืนยันที่จะรับx.size()ฟังก์ชั่นอาจต้องการอินพุต ASCIIZ ดังนั้นจึง.c_str()เป็นตัวเลือกที่ดี (ฟังก์ชั่นจำเป็นต้องรู้ว่าข้อความจะสิ้นสุดที่ใดดังนั้นถ้าไม่ใช่ พารามิเตอร์ที่แยกต่างหากมันสามารถเป็นเพียงการประชุมเช่นความยาวส่วนนำหน้าหรือแมวมองหรือบางความยาวที่คาดว่าจะคงที่)
วิธีการทำให้ตัวชี้ของตัวละครใช้งานได้แม้หลังจากxออกจากขอบเขตหรือมีการแก้ไขเพิ่มเติม
คุณจะต้องคัดลอกเนื้อหาของการที่อยู่นอกพื้นที่หน่วยความจำใหม่string x xบัฟเฟอร์ภายนอกนี้อาจอยู่ในหลาย ๆ สถานที่เช่นstringตัวแปรอาเรย์ตัวอื่นหรืออาจมีหรือไม่มีอายุการใช้งานที่แตกต่างxจากเนื่องจากอยู่ในขอบเขตที่แตกต่างกัน (เช่นเนมสเปซ, โกลบอล, คงที่, ฮีป, หน่วยความจำที่แชร์ .
หากต้องการคัดลอกข้อความจากstd::string xลงในอาร์เรย์อักขระอิสระ:
// 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);
เหตุผลอื่นที่ต้องการchar*หรือconst char*สร้างจากstring
ดังนั้นข้างต้นคุณได้เห็นวิธีการรับ ( const) char*และวิธีทำสำเนาข้อความที่เป็นอิสระจากต้นฉบับstringแต่คุณสามารถทำอะไรกับมันได้บ้าง สุ่มตัวอย่างบางส่วนที่สุ่ม ...
- ให้รหัส "C" ในการเข้าถึงข้อความของ C ++
stringดังเช่นprintf("x is '%s'", x.c_str());
- คัดลอก
xข้อความไปยังบัฟเฟอร์ที่ระบุโดยผู้เรียกฟังก์ชั่นของคุณ (เช่นstrncpy(callers_buffer, callers_buffer_size, x.c_str())) หรือหน่วยความจำชั่วคราวที่ใช้สำหรับอุปกรณ์ I / O (เช่นfor (const char* p = x.c_str(); *p; ++p) *p_device = *p;)
xข้อความต่อท้ายไปยังอาร์เรย์อักขระที่มีข้อความ ASCIIZ บางส่วนแล้ว (เช่นstrcat(other_buffer, x.c_str())) - ระวังอย่าบัฟเฟอร์มากเกินไป (ในหลาย ๆ สถานการณ์ที่คุณอาจจำเป็นต้องใช้strncat)
- ส่งคืน
const char*หรือchar*จากฟังก์ชั่น (อาจเป็นเพราะเหตุผลในอดีต - ลูกค้าใช้ API ที่มีอยู่ของคุณหรือสำหรับความเข้ากันได้กับ C ที่คุณไม่ต้องการส่งคืนstd::stringแต่ต้องการคัดลอกข้อมูลของคุณstringเพื่อโทรหา)
- ระวังอย่าส่งคืนพอยน์เตอร์ที่อาจถูกยกเลิกการลงทะเบียนโดยผู้เรียกหลังจาก
stringตัวแปรโลคอลที่พอยน์เตอร์ชี้นั้นมีขอบเขตอยู่
- บางโครงการที่มีการแชร์ออบเจ็กต์ที่รวบรวม / เชื่อมโยงสำหรับ
std::stringการใช้งานที่แตกต่างกัน(เช่น STLport และคอมไพเลอร์ - เนทีฟ) อาจส่งผ่านข้อมูลเป็น ASCIIZ เพื่อหลีกเลี่ยงความขัดแย้ง