我可以从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
不要感到难过或奇怪,这是完美的风格。
它保证在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;