string.c_str()是const的吗?

16

我有一个库中的函数,它接受一个 char* 并修改数据。

我尝试使用 c_str(),但是 C++ 文档说它返回一个 const char*

除了新建一个 char 数组并将其复制到其中,我还能做什么?

5个回答

17
只要满足以下条件,即可使用&str[0]或者&*str.begin()
  • 通过resize()明确预分配函数所需的所有空间;
  • 函数不尝试超过预分配缓冲区大小(您应该将str.size()作为缓冲区大小的参数);
  • 当函数返回时,显式地在找到第一个\0字符处修剪字符串,否则str.size()将返回“预分配大小”而不是“逻辑”字符串大小。
注意:此方法在C ++11中保证可行(其中字符串保证是连续的),但不适用于标准的先前修订版本;尽管如此,我所知道的标准库实现都没有使用非连续存储来实现std::basic_string
如果您想要更加安全,请使用std::vector<char>(自C++03以来保证是连续的);可以使用构造函数从字符串复制数据(采用接受两个迭代器的构造函数,在末尾添加一个空字符),像使用std::string一样调整其大小,并在找到第一个\0字符时将它复制回一个字符串。

4
注意:这仅在C++11中保证,而在C++03中,字符串可以被实现为绳索(不连续内存块的列表)。 - David Rodríguez - dribeas
1
@DavidRodríguez-dribeas:理论上是正确的,但从未有人这样做过;有人实现了绳索,但作为一个单独的类,我想是为了避免使c_str()调用变得昂贵或者打破所有假定字符串存储在连续内存中的代码。 - Matteo Italia
@Potatoswatter:需要引用来源。 - user784668
1
@Fanael C++11 21.4.3 [string.iterators]没有提到不能修改;而21.4.5 [string.access]和21.4.7.1 [string.accessors]都有提到。 - Potatoswatter
1
@Potatoswatter:我认为在21.4.5/2中存在一些标点符号混淆。我相信“引用不得被修改”仅适用于pos == size()的情况,但这一点并不清楚,因为分号通常比逗号具有更高的优先级。 - Steve Jessop
显示剩余10条评论

11

什么也没有。

因为std::string自己管理其内容,您无法对字符串的底层数据进行写访问。那是未定义行为。

但是,创建和复制char数组并不难:

std::string original("text");
std::vector<char> char_array(original.begin(), original.end());
char_array.push_back(0);

some_function(&char_array[0]);

2
那么我不能安全地执行 &string[0] 吗? - jmasterx
6
只要函数不试图超出缓冲区的长度,实际上传递 &str[0] 应该是明确定义的行为。 - Matteo Italia
5
仅适用于C++11,其中保证了std::string的内容是连续的。 - user784668
2
@kbok:呃,那种方式执行复制不是一个好主意,你没有任何异常安全性;如果你真的想要确保安全,请使用std::vector<char>代替,它保证是连续的并且具有异常安全性。 - Matteo Italia
1
@Potatoswatter:我认为这是为了让字符串实现写时复制。 - slaphappy
显示剩余10条评论

3
如果您知道该函数不会修改超出str.size()的内容,那么可以通过以下方式之一获取指针:
void f( char* p, size_t s ); // update s characters in p
int main() {
   std::string s=...;
   f( &s[0], s.size() );
   f( &s.front(), s.size() );
}

请注意,在C++11中这是有保障的,但在标准的早期版本中允许rope实现(即非连续内存),则不保证。


1
尽管早期标准允许绳索实现,但C++委员会发现从未有过任何实现,这就是为什么他们在C++11中更改了要求。我不会担心它。 - Mark Ransom
1
@Mark:Herb Sutter在他的博客中报道的方式是,他们对在场的人进行了一次草民投票,是否有任何活跃的实现使用绳索。没有,而且在C++0x委员会会议上,没有任何重要的C++实现未被代表。这与彻底调查是否曾经存在过绳索字符串并不完全相同,但正如你所说,在实践中不需要担心它,因为他们提出问题的整个意图是“我们关心的任何实现是否需要更改以支持此附加要求”,答案是否定的。 - Steve Jessop

2
如果实现不尝试增加字符串的长度,则:
C++11:
std::string data = "This is my string.";
func(&*data.begin());

C++03:

 std::string data = "This is my string.";
 std::vector<char> arr(data.begin(), data.end());

 func(&arr[0]);

在 C++03 版本中,func 将被调用,并传入一个未必以空字符结尾的字符串。 - Frerich Raabe

0
这是一个类,它将生成一个临时缓冲区,并在其销毁时自动将其复制到字符串中。
class StringBuffer
{
public:
    StringBuffer(std::string & str) : m_str(str)
    {
        m_buffer.push_back(0);
    }
    ~StringBuffer()
    {
        m_str = &m_buffer[0];
    }
    char * Size(int maxlength)
    {
        m_buffer.resize(maxlength + 1, 0);
        return &m_buffer[0];
    }
private:
    std::string & m_str;
    std::vector<char> m_buffer;
};

以下是如何使用它的方法:

// this is from a crusty old API that can't be changed
void GetString(char * str, int maxlength);

std::string mystring;
GetString(StringBuffer(mystring).Size(MAXLEN), MAXLEN);

如果你觉得你以前见过这段代码,那是因为我从我写的一个问题中复制了它:C++中临时变量的保证生命周期?


1
这看起来像是你重新发明了 std::vector<char> buf(MAXLEN); GetString(&buf[0], MAXLEN); std::string s = &buf[0]; - Frerich Raabe
1
我不喜欢这个类的析构函数可能会抛出异常。 - Steve Jessop
@FrerichRaabe,是的,是的,我做了。 - Mark Ransom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接