给出说...
std::string x = "hello";
从“字符串”中获取“ char *”或“ const char *”
如何获得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
和from &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
的size()
;string
允许包含任意数量的NUL-不会对它们进行特殊处理std::string
(在C ++ 03中相同)。
在C ++ 03中,事情要复杂得多(关键区别突出显示):
x.data()
- 返回标准不要求以NUL
const char*
结尾的字符串的内部缓冲区(即,可能在其后跟随未初始化或垃圾值,并且意外访问具有未定义的行为)。
['h', 'e', 'l', 'l', 'o']
x.size()
字符是安全阅读的,即x[0]
通过x[x.size() - 1]
- 对于空字符串,可以确保可以安全地添加0的某些非NULL指针(欢呼!),但是您不应该取消引用该指针。
&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 Standard的措辞是允许字符串实现自由地创建一个独立的以NUL终止的缓冲区 ,该缓冲区是由
x.data()
and所“暴露”的潜在非NUL终止的缓冲区。&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 ++的函数(如say)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
)
- 从函数返回a
const char*
或(char*
从历史上讲-客户端正在使用您的现有API-或出于C兼容性,您不想返回a std::string
,但希望将其string
数据复制到调用方的某个位置)
- 注意不要在指针指向的局部
string
变量离开作用域之后返回调用者可能取消引用的指针
- 一些为不同的
std::string
实现而编译/链接了共享对象的项目(例如STLport和native编译器)可以将数据作为ASCIIZ传递以避免冲突