我可以从C ++字符串中获取非const C字符串吗?

C ++中的const-correctness仍让我感到头疼。 在使用一些旧的C代码时,我发现自己需要将C ++字符串对象转换为C字符串并将其分配给变量。 但是,变量是char *c_str()返回const char [] 。 有没有一个很好的方法来解决这个问题,而不必自己动手去做呢?

编辑:我也试图避免调用新的。 我很乐意交换稍微复杂的代码以减少内存泄漏。

我想总有strcpy

或者在C ++代码的部分中使用char*字符串,这些字符串必须与旧的东西接口。

或者重构现有代码以使用C ++编译器进行编译,然后使用std:string

因为对我来说莫名其妙的原因没有人像我现在这样回答这个问题,而且由于其他问题现在正在关闭这个问题,我会在这里加上这个,即使过了一年太晚意味着它会挂在堆的底部……


使用C ++ 03时, std::string不能保证将其字符存储在连续的内存中,并且c_str()的结果不需要指向字符串的内部缓冲区,因此唯一的方法是保证工作是这样的:

 std::vector buffer(s.begin(), s.end()); foo(&buffer[0], buffer.size()); s.assign(buffer.begin(), buffer.end()); 

由于没有已知的实现实际使用非连续内存,并且由于std::string被许多人使用,好像这是有保证的,因此规则将针对即将推出的标准进行更改,现在保证其字符的连续内存。 所以在C ++ 1x中,这应该工作:

 foo(&s[0], s.size()); 

然而,这需要注意: &s[0]的结果(作为s.c_str() ,BTW的结果)仅保证在调用可能更改字符串的任何成员函数之前有效。 所以你不应该把这些操作的结果存储在任何地方。 最安全的是在完整表达结束时用它们完成,就像我的例子那样。

你需要在这里做一个重要的区别:你想给它指定“道德常数”的char* ? 也就是说,抛弃const -ness只是一种技术性,你真的仍然会将字符串视为const吗? 在这种情况下,您可以使用const_cast – 无论是C风格还是C ++风格的const_cast 。 只要你(以及任何曾经维护过此代码的人)都有纪律将char*视为const char* ,你会没事的,但是编译器将不会再看你的背,所以如果你曾经对待过它作为一个非const你可能正在修改一个缓冲区,代码中的其他东西依赖于它。

如果您的char*将被视为非const ,并且您打算修改它指向的内容,则必须复制返回的字符串,而不是丢弃其const -ness。

const -cast总是……

 std::string s("hello world"); char *p = const_cast(s.c_str()); 

当然,这基本上颠覆了类型系统,但有时在与旧代码集成时是必要的。

您可以使用复制方法:

 len = myStr.copy(cStr, myStr.length()); cStr[len] = '\0'; 

其中myStr是你的C ++字符串, cStr是一个char * ,至少是myStr.length() +1大小。 此外, len的类型为size_t并且是必需的,因为copy不会终止cStr

如果你能负担额外的分配,而不是推荐的strcpy我会考虑使用std::vector如下所示:

 // suppose you have your string: std::string some_string("hello world"); // you can make a vector from it like this: std::vector some_buffer(some_string.begin(), some_string.end()); // suppose your C function is declared like this: // some_c_function(char *buffer); // you can just pass this vector to it like this: some_c_function(&some_buffer[0]); // if that function wants a buffer size as well, // just give it some_buffer.size() 

对我来说,这比strcpy更多的是C ++方式。 看看Meyers的有效STL第16项,可以获得比我所能提供的更好的解释。

如果您知道std::string不会改变,那么C风格的转换将起作用。

 std::string s("hello"); char *p = (char *)s.c_str(); 

当然, p指向由std::string管理的某个缓冲区。 如果std::string超出范围或缓冲区被更改(即写入),则p可能无效。

最安全的做法是复制字符串,如果重构代码是不可能的。

如果c_str()返回给你一个字符串对象内部缓冲区的副本,你可以使用const_cast<>

但是,如果c_str()让您直接访问字符串对象内部缓冲区,则进行显式复制,而不是删除const。

由于c_str()为您提供了对数据结构的直接const访问,因此您可能不应该强制转换它。 在不必预先分配缓冲区的情况下,最简单的方法就是使用strdup。

 char* tmpptr; tmpptr = strdup(myStringVar.c_str(); oldfunction(tmpptr); free tmpptr; 

它快速,简单,正确。

 std::string vString; vString.resize(256); // allocate some space, up to you char* vStringPtr(&vString.front()); // assign the value to the string (by using a function that copies the value). // don't exceed vString.size() here! // now make sure you erase the extra capacity after the first encountered \0. vString.erase(std::find(vString.begin(), vString.end(), 0), vString.end()); // and here you have the C++ string with the proper value and bounds. 

这是将C ++字符串转换为C字符串的方法。 但请确保您知道自己在做什么,因为使用原始字符串函数很容易走出界限。 有时候这是必要的。

只需使用const_cast(str.data())

不要感到难过或奇怪,这是完美的风格。

它保证在C ++ 11中工作。 它的const合格的事实可以说是它之前的原始标准的错误; 在C ++ 03中,有可能将string实现为不连续的内存列表,但没有人做过。 世界上没有一个编译器将string实现为连续内存块以外的任何内容,因此请完全自信地对待它。

做自己真的很难吗?

 #include  #include  char *convert(std::string str) { size_t len = str.length(); char *buf = new char[len + 1]; memcpy(buf, str.data(), len); buf[len] = '\0'; return buf; } char *convert(std::string str, char *buf, size_t len) { memcpy(buf, str.data(), len - 1); buf[len - 1] = '\0'; return buf; } // A crazy template solution to avoid passing in the array length // but loses the ability to pass in a dynamically allocated buffer template  char *convert(std::string str, char (&buf)[len]) { memcpy(buf, str.data(), len - 1); buf[len - 1] = '\0'; return buf; } 

用法:

 std::string str = "Hello"; // Use buffer we've allocated char buf[10]; convert(str, buf); // Use buffer allocated for us char *buf = convert(str); delete [] buf; // Use dynamic buffer of known length buf = new char[10]; convert(str, buf, 10); delete [] buf;