【问题标题】:string.c_str() is const? [duplicate]string.c_str() 是常量吗? [复制]
【发布时间】:2012-06-10 14:53:42
【问题描述】:

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

我试图给它c_str(),但 c++ 文档说它返回一个const char*

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

【问题讨论】:

    标签: c++


    【解决方案1】:

    您可以使用&str[0]&*str.begin(),只要:

    • 您使用resize() 显式预分配函数所需的所有空间;
    • 函数不会尝试超过预先分配的缓冲区大小(您应该将str.size() 作为缓冲区大小的参数传递);
    • 当函数返回时,您在找到的第一个\0 字符处显式修剪字符串,否则str.size() 将返回“预分配大小”而不是“逻辑”字符串大小。

    注意:这保证在 C++11 中有效(其中字符串保证是连续的),但在标准的先前版本中不可用;尽管如此,我所知道的标准库的任何实现都没有使用非连续存储实现std::basic_string

    不过,如果您想安全起见,请使用std::vector<char>(从 C++03 开始​​保证是连续的);用你想要的任何东西进行初始化(你可以使用带有两个迭代器的构造函数从字符串中复制它的数据,最后添加一个空字符),像使用std::string一样调整它的大小,然后将它复制回一个停止在的字符串第一个 \0 字符。

    【讨论】:

    • 注意:这仅在 C++11 中得到保证,在 C++03 中,字符串可以实现为绳索(不连续的内存块列表)
    • @DavidRodríguez-dribeas:理论上是正确的,但从来没有人这样做过;有人实施了绳索,但作为一个单独的班级,我想避免例如使c_str() 调用昂贵或破坏所有假设字符串存储在连续内存中的代码。
    • 为了安全起见,您还可以在将任何内容传递给 C 函数之前,通过在其末尾添加一个 0 字符来显式地终止您的 string。这样一来,就不用担心如果 C 函数写入它找到的终止符会发生什么。
    • @Fanael C++11 21.4.3 [string.iterators] 没有提到不修改; 21.4.5 [string.access] 和 21.4.7.1 [string.accessors] 都可以。
    • @Potatoswatter:我认为21.4.5/2 中有一些标点符号混淆。我认为“不得修改引用”旨在仅适用于pos == size()的情况,但从分号中不清楚。这很令人困惑,因为通常在英语中,逗号的优先级高于分号。
    【解决方案2】:

    什么都没有。

    由于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]);
    

    【讨论】:

    • 所以我不能安全地做 &string[0]?
    • 其实传递&amp;str[0]应该是明确的行为,只要函数不尝试超过缓冲区的长度。
    • @MatteoItalia:仅在 C++11 中,保证std::string 的内容是连续的。
    • @MatteoItalia 否,C++03 允许不连续的 string 实现,但没有人发现这种实用性,所以他们改变了,以支持用户的灵活性和实现的刚性。
    • @kbok:呃,那样执行副本不是一个好主意,您没有任何异常安全性;如果你真的想确保使用std::vector&lt;char&gt;,它保证是连续的并且是异常安全的。
    【解决方案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 实现(即非连续内存)的标准的先前版本中没有保证

    【讨论】:

    • 虽然早期的标准允许绳索实现,但 C++ 委员会发现从未实现过,这就是他们更改 C++11 要求的原因。我不会担心的。
    • @Mark:Herb Sutter 在他的博客上报道的方式,他们对在场的人进行了一次民意调查,是否有任何积极的实施使用了绳索。没有,在 C++0x 委员会会议上也没有没有重要的 C++ 实现。这与彻底调查是否曾经有绳索并不完全相同,但正如你所说,在实践中不必担心它就足够了,因为他们问题的重点是“将我们关心的任何实现都需要更改以支持这个额外的要求”,答案是否定的。
    【解决方案4】:

    如果您的实现不会尝试增加字符串的长度,那么:

    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 将被调用一个不一定以空结尾的字符串。
    【解决方案5】:

    这是一个类,它会生成一个临时缓冲区,并在它被销毁时自动将其复制到字符串中。

    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);
    

    如果你认为你以前看过这段代码,那是因为我从我写的一个问题中复制了它:Guaranteed lifetime of temporary in C++?

    【讨论】:

    • 这看起来像是你重新发明了std::vector&lt;char&gt; buf(MAXLEN); GetString(&amp;buf[0], MAXLEN); std::string s = &amp;buf[0];
    • 我不喜欢这个类的析构函数可以抛出。
    • @FrerichRaabe,是的,是的。
    猜你喜欢
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-21
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    • 1970-01-01
    • 2012-07-31
    相关资源
    最近更新 更多