你可以使用 &mystring[0] 获得一个 char * 指针,但是有一些问题's: you won' t必然会得到一个零终止的字符串,并且你赢得了't be able to change the string'的大小 . 您尤其要注意不要在字符串末尾添加字符,否则会出现缓冲区溢出(以及可能的崩溃) .
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
使用 .data() 与其他程序员进行通信,读取数据不是ASCIIZ的源代码(相反,你甚至真的是文本)),或者're passing it to another function that treats it as a block of 101245 data. This can be a crucial insight in ensuring that other programmers'代码更改继续正确处理数据 . 仅
C 03:您的 string 实现可能需要进行一些额外的内存分配和/或数据复制才能准备NUL终止缓冲区
您需要 copystringx 的内容到 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);
给"C"代码访问C string 的文本,如 printf("x is '%s'", x.c_str());
copy x 's text to a buffer specified by your function' s来电(例如 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* (可能是出于历史原因 - 客户端's using your existing API - or for C compatibility you don' t想要返回 std::string ,但是想要复制 string 的数据在某个地方为来电者)
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;
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::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');
// get the char* using &writable[0] or &*writable.begin()
8 回答
试试这个
看看这个:
但请注意,这将返回
const char *
. 对于char *
,请使用strcpy
将其复制到另一个char
数组中 ..c_str()
使用.c_str()
方法 .你可以使用
&mystring[0]
获得一个char *
指针,但是有一些问题's: you won' t必然会得到一个零终止的字符串,并且你赢得了't be able to change the string'的大小 . 您尤其要注意不要在字符串末尾添加字符,否则会出现缓冲区溢出(以及可能的崩溃) .在C 11之前,无法保证所有字符都是同一个连续缓冲区的一部分,但实际上所有已知的
std::string
实现都是这样的 . 见Does “&s[0]” point to contiguous characters in a std::string? .请注意,许多
string
成员函数将重新分配内部缓冲区并使您可能已保存的任何指针无效 . 最好立即使用它们然后丢弃 .C 17
C++17 (即将推出的标准)更改模板的概要
basic_string
添加data()
的非常量重载:来自std :: basic_string <CharT>的CharT const *
来自std :: basic_string <CharT>的CharT *
C 11
来自std :: basic_string <CharT>的CharT const *
来自std :: basic_string <CharT>的CharT *
从C 11开始,标准说:
有可分割的方法来获取非const字符指针 .
1.使用C 11的连续存储
Pro
简单而简短
快速(仅涉及无复制的方法)
Cons
'\0'
不得更改/不一定是非常量内存的一部分 .2.使用std :: vector <CharT>
Pro
简单
自动内存处理
动态
Cons
3.如果N是编译时间常量(并且足够小),则使用std :: array <CharT,N>
Pro
简单
堆栈内存处理
Cons
静态
需要字符串副本
4.具有自动存储删除功能的原始内存分配
Pro
内存占用量小
自动删除
简单
Cons
需要字符串副本
静态(动态使用需要更多代码)
比矢量或数组少的功能
5.具有手动处理的原始内存分配
Pro
Con
需要字符串副本
错误的最大责任/易感性
复杂
我正在使用一个带有很多函数的API作为输入
char*
.我创建了一个小班来面对这类问题,我已经实现了RAII成语 .
你可以用它作为:
我已经调用了类
DeepString
,因为它正在创建现有字符串的深层且唯一的副本(DeepString
不可复制) .鉴于......
从
string
获取char *
或const char *
How to get a character pointer that's valid while x remains in scope and isn't modified further
C++11 简化了事情;以下所有内容都允许访问相同的内部字符串缓冲区:
所有上述指针都将保持相同的值 - 缓冲区中第一个字符的地址 . 即使空字符串也有"first character in the buffer",因为C 11保证在显式分配的字符串内容之后始终保留额外的NUL / 0终结符字符(例如
std::string("this\0that", 9)
将具有保存"this\0that\0"
的缓冲区) .给出以上任何指针:
仅适用于来自
&x[0]
的非const
指针:在字符串的其他地方写一个NUL不会改变
string
的size()
;string
被允许包含任意数量的NUL--它们没有被std::string
给予特殊处理(在C 03中也是如此) .在 C++03 中,事情要复杂得多(关键差异 highlighted ):
x.data()
将
const char*
返回到字符串的内部缓冲区 which wasn't required by the Standard to conclude with a NUL (即可能是['h', 'e', 'l', 'l', 'o']
后跟未初始化或垃圾值,其中意外访问具有未定义的行为) .x.size()
个字符可以安全阅读,即x[0]
至x[x.size() - 1]
对于空字符串,你're guaranteed some non-NULL pointer to which 0 can be safely added (hurray!), but you shouldn' t取消引用那个指针 .
&x[0]
for empty strings this has undefined behaviour (21.3.4)
例如给
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
时x.empty()
时不能调用f(&x[0], x.size());
- 只需使用f(x.data(), ...)
.否则,根据
x.data()
但是:for non
const
x
这会产生非const
char*
指针;你可以覆盖字符串内容x.c_str()
将
const char*
返回到值的ASCIIZ(NUL终止)表示(即['h','e','l','l','o','\0']) .尽管很少有任何实现选择这样做,但是C 03标准的措辞是允许字符串实现自由地创建 distinct NUL-terminated buffer ,从潜在的非NUL终止缓冲区"exposed"到
x.data()
和&x[0]
x.size()
1个字符可以安全阅读 .即使对于空字符串也保证安全(['\0']) .
访问外部法律索引的后果
无论您获得指针的方式如何,您都不能从指针进一步访问内存,而不是上面描述中保证的字符 . 尝试这样做有未定义的行为,即使对于读取,也存在非常真实的应用程序崩溃和垃圾结果的可能性,此外还有批量数据,堆栈损坏和/或写入的安全漏洞 .
这些指针什么时候失效?
如果调用
string
成员函数来修改string
或保留更多容量,则任何上述方法预先返回的任何指针值都将失效 . 您可以再次使用这些方法来获取另一个指针 . (规则与string
中的迭代器的规则相同) .另请参见如何在
x
保留范围或在下面进一步修改后使字符指针有效....那么,哪个更好用?
从C 11开始,对于ASCIIZ数据使用
.c_str()
,对于"binary"数据使用.data()
(在下面进一步说明) .在C 03中,使用
.c_str()
,除非确定.data()
足够,并且更喜欢.data()
而不是&x[0]
,因为它对于空字符串是安全的.......尝试理解程序足够在适当的时候使用
data()
,否则你可能会犯其他错误......由
.c_str()
保证的ASCII NUL '\0'字符被许多函数用作表示相关和安全访问数据结束的标记值 . 这适用于C -only函数,例如fstream::fstream(const char* filename, ...)
和共享C函数,如strchr()
和printf()
.鉴于C 03的
.c_str()
对返回缓冲区的保证是.data()
的超集,你可以随时安全地使用.c_str()
,但人们有时不这样做,因为:.data()
与其他程序员进行通信,读取数据不是ASCIIZ的源代码(相反,你甚至真的是文本)),或者're passing it to another function that treats it as a block of 101245 data. This can be a crucial insight in ensuring that other programmers'代码更改继续正确处理数据 .仅
string
实现可能需要进行一些额外的内存分配和/或数据复制才能准备NUL终止缓冲区作为进一步提示,如果函数的参数需要(
const
)char*
但不坚持获取x.size()
,则该函数可能需要ASCIIZ输入,因此.c_str()
是一个不错的选择(函数需要知道文本以某种方式终止的位置,因此,如果它不是一个单独的参数,它只能是一个像长度前缀或标记或一些固定的预期长度的约定 .如果x离开作用域或进一步修改后,如何使字符指针有效
您需要 copy
string
x
的内容到x
之外的新内存区域 . 此外部缓冲区可能位于许多位置,例如另一个string
或字符数组变量,由于处于不同的范围(例如命名空间,全局,静态,堆,共享内存,内存映射),它可能会或可能不会具有与x
不同的生命周期文件) .要将
std::string x
中的文本复制到独立的字符数组中:想要从字符串生成char *或const char *的其他原因
所以,上面你已经看到了如何获得(
const
)char*
,以及如何制作独立于原始string
的文本副本,但是你能用它做什么呢?一个随机的例子......给"C"代码访问C
string
的文本,如printf("x is '%s'", x.c_str());
copy
x
's text to a buffer specified by your function' s来电(例如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*
(可能是出于历史原因 - 客户端's using your existing API - or for C compatibility you don' t想要返回std::string
,但是想要复制string
的数据在某个地方为来电者)注意不要在指针指向的本地
string
变量已经离开范围之后返回可能被调用者取消引用的指针为不同的
std::string
实现编译/链接的一些具有共享对象的项目(例如STLport和编译器本机)可以将数据作为ASCIIZ传递以避免冲突如果您只想将std::string传递给需要
const char*
的函数,则可以使用如果你想获得一个可写的副本,比如
char *
,你可以这样做:Edit :请注意,上述内容并非异常安全 . 如果
new
调用和delete
调用之间的任何内容抛出,则会泄漏内存,因为没有任何内容会自动为您调用delete
. 有两种直接的方法可以解决这个问题 .boost :: scoped_array
boost::scoped_array将在超出范围时为您删除内存:
std :: vector
这是标准方式(不需要任何外部库) . 您使用std::vector,它可以为您完全管理内存 .