ให้พูด ...
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
หรือสำรองความจุเพิ่มเติมค่าตัวชี้ใด ๆ กลับมาก่อนโดยใด ๆ ของวิธีการดังกล่าวเป็นโมฆะ คุณสามารถใช้วิธีการเหล่านั้นอีกครั้งเพื่อรับตัวชี้อื่น (กฎนั้นเหมือนกันสำหรับตัววนซ้ำในstring
s)
ดูเพิ่มเติมวิธีการรับตัวชี้อักขระที่ถูกต้องแม้หลังจาก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 เพื่อหลีกเลี่ยงความขัดแย้ง