วิธีการแปลง std :: string เป็น const char * หรือ char *?


893

ฉันจะแปลงเป็นstd::stringa char*หรือ a ได้const char*อย่างไร?


2
แทนที่จะเป็น: ถ่าน * เขียนได้ = ถ่านใหม่ [str.size () + 1]; คุณสามารถใช้ถ่านที่เขียนได้ [str.size () + 1]; จากนั้นคุณไม่ต้องกังวลเกี่ยวกับการลบการจัดการที่เขียนได้หรือข้อยกเว้น

7
คุณไม่สามารถใช้ str.size () เว้นแต่ว่าขนาดจะรู้จักกันในเวลารวบรวมนอกจากนี้มันยังอาจล้นสแต็กของคุณหากค่าขนาดคงที่มีขนาดใหญ่
paulm

1
ถ่าน * ผลลัพธ์ = strcpy ((ถ่าน *) malloc (str.length () + 1), str.c_str ());
cegprakash

7
@cegprakash strcpyและmallocไม่ใช่วิธี C ++
boycy

4
ไม่ แต่char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)จะเป็นสำนวน C ++ ที่มากกว่า strcpy()และmalloc()ไม่ผิดหรือเป็นปัญหา แต่ดูเหมือนว่าไม่สอดคล้องกันที่จะใช้สตริง C ++ และสิ่งอำนวยความสะดวกไลบรารี C ที่มี C ++ เทียบเท่าในบล็อกรหัสเดียวกัน
boycy

คำตอบ:


1055

หากคุณเพียงแค่ต้องการส่งผ่านstd::stringไปยังฟังก์ชั่นที่ต้องการconst char*คุณสามารถใช้

std::string str;
const char * c = str.c_str();

หากคุณต้องการได้รับสำเนาที่เขียนได้เช่นchar *นี้คุณสามารถทำสิ่งนี้ได้:

std::string str;
char * writable = new char[str.size() + 1];
std::copy(str.begin(), str.end(), writable);
writable[str.size()] = '\0'; // don't forget the terminating 0

// don't forget to free the string after finished using it
delete[] writable;

แก้ไข : ขอให้สังเกตว่าข้างต้นไม่ปลอดภัยยกเว้น หากมีสิ่งใดเกิดขึ้นระหว่างการnewโทรและการdeleteโทรคุณจะรั่วไหลของหน่วยความจำเนื่องจากจะไม่มีการโทรdeleteหาคุณโดยอัตโนมัติ มีสองวิธีในการแก้ไขปัญหานี้ทันที

เพิ่ม :: scoped_array

boost::scoped_array จะลบหน่วยความจำสำหรับคุณเมื่อออกนอกขอบเขต:

std::string str;
boost::scoped_array<char> writable(new char[str.size() + 1]);
std::copy(str.begin(), str.end(), writable.get());
writable[str.size()] = '\0'; // don't forget the terminating 0

// get the char* using writable.get()

// memory is automatically freed if the smart pointer goes 
// out of scope

Std :: เวกเตอร์

นี่เป็นวิธีมาตรฐาน (ไม่ต้องใช้ไลบรารีภายนอก) คุณใช้std::vectorซึ่งจัดการหน่วยความจำให้คุณอย่างสมบูรณ์

std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');

// get the char* using &writable[0] or &*writable.begin()

41
เพียงใช้ char * result = strdup (str.c_str ());
Jasper Bekkers

63
คุณทำได้, แต่ strdup ไม่ใช่ ac หรือ c ++ ฟังก์ชั่นมาตรฐาน, มันมาจาก posix :)
Johannes Schaub - litb

14
สิ่งที่ฉันอาจจะชอบโดยทั่วไปคือ std :: vector <char> เขียนได้ (str.begin (), str.end ()); writable.push_back ( '\ 0'); ถ่าน * c = & เขียนได้ [0];
Johannes Schaub - litb

17
std :: copy เป็นวิธี c ++ ในการทำเช่นนี้โดยไม่จำเป็นต้องไปที่ตัวชี้สตริง ฉันพยายามหลีกเลี่ยงการใช้ฟังก์ชั่น C มากที่สุดเท่าที่จะทำได้
Johannes Schaub - litb

16
ในฐานะของ C ++ 17 std::string::data()ตอนนี้ส่งกลับแทนCharT* const CharT*อาจเป็นความคิดที่ดีที่จะอัปเดตคำตอบนี้ :)
Rakete1111

192

ให้พูด ...

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 เพื่อหลีกเลี่ยงความขัดแย้ง

4
ทำได้ดีนี่. อีกเหตุผลที่ต้องการ char * (ไม่ใช่ const) คือการใช้งานกับการออกอากาศ MPI ดูเหมือนดีกว่าถ้าคุณไม่ต้องคัดลอกไปมา ฉันจะเสนอ char * const getter ให้กับสตริงเป็นการส่วนตัว ตัวชี้ Const แต่เป็นสตริงที่แก้ไขได้ แม้ว่ามันอาจจะสับสนกับการแปลงโดยปริยายจากถ่าน const * สตริง ...
bartgol

33

ใช้วิธีการ.c_str()const char *

คุณสามารถใช้&mystring[0]เพื่อรับchar *ตัวชี้ แต่มีคู่ของ gotcha: คุณไม่จำเป็นต้องได้รับสตริงที่สิ้นสุดแล้วและคุณจะไม่สามารถเปลี่ยนขนาดของสตริงได้ โดยเฉพาะอย่างยิ่งคุณจะต้องระมัดระวังไม่ให้เพิ่มตัวละครจนจบสตริงหรือคุณจะได้รับบัฟเฟอร์มากเกินไป (และอาจเกิดความผิดพลาดได้)

ไม่มีการรับประกันว่าตัวละครทุกตัวจะเป็นส่วนหนึ่งของบัฟเฟอร์ที่ต่อเนื่องกันจนถึง C ++ 11 แต่ในทางปฏิบัติแล้วการใช้งานที่รู้จักทั้งหมดของการstd::stringทำงานแบบนั้นอยู่ดี; เห็น“ & s [0]” ชี้ไปที่อักขระที่อยู่ติดกันในสตริง std :: หรือไม่ .

โปรดทราบว่าstringฟังก์ชั่นสมาชิกจำนวนมากจะทำการจัดสรรบัฟเฟอร์ภายในใหม่และทำให้ตัวชี้ใด ๆ ที่คุณอาจบันทึกไว้ไม่ถูกต้อง ที่ดีที่สุดที่จะใช้พวกเขาทันทีและจากนั้นทิ้ง


1
คุณควรทราบว่า data () ส่งคืน const char * :) สิ่งที่คุณหมายถึงคือ & str [0] ซึ่งส่งคืนสตริงที่ต่อเนื่องกัน แต่ไม่ใช่สตริงที่ยกเลิกด้วยค่า null ที่ไม่จำเป็น
Johannes Schaub - litb

1
@litb, อ๊าก! นั่นคือสิ่งที่ฉันพยายามจะตอบคำตอบอย่างรวดเร็ว ฉันเคยใช้วิธีแก้ปัญหาของคุณในอดีตไม่รู้ว่าทำไมมันไม่ใช่สิ่งแรกที่นึกถึง ฉันแก้ไขคำตอบของฉันแล้ว
Mark Ransom

2
ในทางเทคนิค std :: storage string จะต่อเนื่องกันใน C ++ 0x เท่านั้น
MSalters

1
@ ผู้ทำลายขอบคุณ - ฉันไม่รู้เหมือนกัน ฉันถูกกดยากที่จะค้นหาการใช้งานในกรณีที่ไม่เป็นเช่นนั้น
Mark Ransom

2
ถ่าน * ผลลัพธ์ = strcpy (malloc (str.length () + 1), str.c_str ());
cegprakash

21

C ++ 17

C ++ 17 (มาตรฐานที่กำลังจะมาถึง) เปลี่ยนการสรุปของเทมเพลตที่basic_stringเพิ่มการโอเวอร์โหลดที่ไม่ใช่ const ของdata():

charT* data() noexcept;

คืนค่า: ตัวชี้ p ซึ่ง p + i == & ตัวดำเนินการสำหรับแต่ละ i ใน [0, size ()]


CharT const * จาก std::basic_string<CharT>

std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()

CharT * จาก std::basic_string<CharT>

std::string str = { "..." };
char * p = str.data();

C ++ 11

CharT const * จาก std::basic_string<CharT>

std::string str = { "..." };
str.c_str();

CharT * จาก std::basic_string<CharT>

จาก C ++ 11 เป็นต้นไปมาตรฐานบอกว่า:

  1. วัตถุที่มีลักษณะคล้ายถ่านในbasic_stringวัตถุจะถูกเก็บไว้อย่างต่อเนื่อง นั่นคือสำหรับbasic_stringวัตถุใด ๆ ที่sเป็นตัวตน&*(s.begin() + n) == &*s.begin() + nให้ดำรงค่าทั้งหมดของดังกล่าวว่าn0 <= n < s.size()

  1. const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

    คืนค่า: *(begin() + pos)ถ้าpos < size()มิฉะนั้นการอ้างอิงไปยังวัตถุของการพิมพ์ที่CharTมีมูลค่าCharT(); ค่าอ้างอิงจะไม่สามารถแก้ไขได้


  1. const charT* c_str() const noexcept;
    const charT* data() const noexcept;

    ผลตอบแทน: ตัวชี้หน้าดังกล่าวว่าp + i == &operator[](i)สำหรับแต่ละในi[0,size()]

มีวิธีที่เป็นไปได้อย่างรุนแรงที่จะได้รับตัวชี้ที่ไม่ใช่ const

1. ใช้ที่เก็บข้อมูลต่อเนื่องของ C ++ 11

std::string foo{"text"};
auto p = &*foo.begin();

มือโปร

  • ง่ายและสั้น
  • เร็ว (วิธีเดียวที่ไม่มีการทำสำเนา)

จุดด้อย

  • สุดท้าย '\0'ไม่ควรถูกแก้ไข / ไม่จำเป็นต้องเป็นส่วนหนึ่งของหน่วยความจำที่ไม่ใช่ const

2. ใช้ std::vector<CharT>

std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();

มือโปร

  • ง่าย
  • การจัดการหน่วยความจำอัตโนมัติ
  • พลวัต

จุดด้อย

  • ต้องมีการคัดลอกสตริง

3. ใช้std::array<CharT, N>ถ้าNรวบรวมเวลาคงที่ (และเล็กพอ)

std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());

มือโปร

  • ง่าย
  • การจัดการหน่วยความจำสแต็ค

จุดด้อย

  • คงที่
  • ต้องมีการคัดลอกสตริง

4. การจัดสรรหน่วยความจำแบบ Raw พร้อมการลบที่เก็บข้อมูลอัตโนมัติ

std::string foo{ "text" };
auto p = std::make_unique<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);

มือโปร

  • หน่วยความจำขนาดเล็ก
  • การลบอัตโนมัติ
  • ง่าย

จุดด้อย

  • ต้องมีการคัดลอกสตริง
  • คงที่ (การใช้งานแบบไดนามิกต้องใช้รหัสมากขึ้น)
  • คุณสมบัติน้อยกว่าเวกเตอร์หรืออาเรย์

5. การจัดสรรหน่วยความจำแบบ Raw พร้อมการจัดการด้วยตนเอง

std::string foo{ "text" };
char * p = nullptr;
try
{
  p = new char[foo.size() + 1u];
  std::copy(foo.data(), foo.data() + foo.size() + 1u, p);
  // handle stuff with p
  delete[] p;
}
catch (...)
{
  if (p) { delete[] p; }
  throw;
}

มือโปร

  • 'การควบคุม' สูงสุด

แย้ง

  • ต้องมีการคัดลอกสตริง
  • ความรับผิด / ความเสี่ยงสูงสุดสำหรับข้อผิดพลาด
  • ซับซ้อน

9

ฉันกำลังทำงานกับ API char*ที่มีจำนวนมากของฟังก์ชั่นได้รับเป็นผู้ใส่

ฉันได้สร้างชั้นเรียนขนาดเล็กเพื่อเผชิญกับปัญหาเช่นนี้ฉันได้ใช้คำว่า RAII

class DeepString
{
        DeepString(const DeepString& other);
        DeepString& operator=(const DeepString& other);
        char* internal_; 

    public:
        explicit DeepString( const string& toCopy): 
            internal_(new char[toCopy.size()+1]) 
        {
            strcpy(internal_,toCopy.c_str());
        }
        ~DeepString() { delete[] internal_; }
        char* str() const { return internal_; }
        const char* c_str()  const { return internal_; }
};

และคุณสามารถใช้มันเป็น:

void aFunctionAPI(char* input);

//  other stuff

aFunctionAPI("Foo"); //this call is not safe. if the function modified the 
                     //literal string the program will crash
std::string myFoo("Foo");
aFunctionAPI(myFoo.c_str()); //this is not compiling
aFunctionAPI(const_cast<char*>(myFoo.c_str())); //this is not safe std::string 
                                                //implement reference counting and 
                                                //it may change the value of other
                                                //strings as well.
DeepString myDeepFoo(myFoo);
aFunctionAPI(myFoo.str()); //this is fine

ฉันเรียกคลาสนี้DeepStringเพราะเป็นการสร้างสำเนาที่ลึกและไม่ซ้ำใคร (ไม่สามารถคัดลอกDeepStringได้) ของสตริงที่มีอยู่


3
ฉันจะหลีกเลี่ยงข้อตกลงการตั้งชื่อนี้ c_str()ตามที่ใช้stdเป็นตัวย่อของ "C-string" ไม่ใช่ "const string" และstr()จะส่งคืน a std::basic_string, ไม่ใช่char*(ตัวอย่างstd::stringstream::str())
bcrist

8
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());

1
ดูแฟนซี แต่เข้าใจยากจริงๆ ... ความเรียบง่ายคือ IMO ที่ดีที่สุด
Naeem A. Malik

4
strcpy (), malloc (), length () และ c_str () เป็นฟังก์ชั่นพื้นฐานและไม่มีอะไรยากในเรื่องนี้ เพียงจัดสรรหน่วยความจำและคัดลอก
cegprakash

5
ใช่ฟังก์ชั่นพื้นฐาน แต่คุณได้บิดและงอพวกเขาให้ดูเหมือนชามสปาเก็ตตี้หรือสัตว์ประหลาดตัวหนึ่งของแฟรงเกนสไตน์ :)
Naeem A. Malik

4
ใช่ฟังก์ชั่นพื้นฐาน แต่ ...คุณจำได้ไหมเมื่อคุณเริ่มจัดการกับภาษาการเขียนโปรแกรม? เส้นบางมากขึ้นในการอธิบายและมันจริงๆจะช่วยให้เณรที่จะเรียนรู้ว่าทำไมตัวอย่างที่แตกต่างกันหรือดีกว่าคำตอบนี้ :)
แฮสเธอร์

2
@cegprakash: เมื่อใดก็ตามที่มี malloc () จะต้องมีฟรีด้วย () มิฉะนั้นโค้ดรั่วหน่วยความจำและวิธีการแก้ปัญหาในคำตอบของคุณ การจัดสรรหน่วยความจำโดยที่อย่างน้อยบอกใบ้ถึงการจัดสรรคืนที่ต้องการเป็นการปฏิบัติที่ไม่ดีสำหรับคำถามดังกล่าว
Striezel

7

เพิ่งเห็นสิ่งนี้:

string str1("stackoverflow");
const char * str2 = str1.c_str();

อย่างไรก็ตามโปรดทราบว่าสิ่งนี้จะส่งคืน const char *อย่างไรก็ตามทราบว่านี้จะกลับ

สำหรับ a char *ใช้strcpyเพื่อคัดลอกไปยังcharอาร์เรย์อื่น


23
สวัสดีสิ่งที่คุณโพสต์พูดไปแล้วหลายครั้งโดยมีรายละเอียดเพิ่มเติมในคำตอบอื่น ๆ สำหรับคำถามอายุ 5 ปี การตอบคำถามรุ่นเก่านั้นทำได้ดี แต่ถ้าคุณเพิ่มข้อมูลใหม่ มิฉะนั้นมันเป็นเพียงเสียงรบกวน
Mat

7
ส่วนตัวผมชื่นชมความเรียบง่าย
TankorSmash

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.