【问题标题】:Does the address of the result of std::string::operator[] point to a writable, nul-terminated buffer?std::string::operator[] 结果的地址是否指向可写的、以 nul 结尾的缓冲区?
【发布时间】:2012-04-01 11:25:03
【问题描述】:

我正在修改一个接受 const char* 并使用函数 ProcessString 的函数。 ProcessString 是一个函数,它需要一个以空字符结尾的字符缓冲区作为 char*。缓冲区中的字符可能会或可能不会被修改,如下面的函数签名所定义。为了“弥合差距”,我使用了一个临时的 std::string:

void ProcessString( char* str );

void SomeFunction( const char* str )
{
  string temp(str);
  ProcessString( &temp[0] );
}

我的主要问题是关于 std::string::operator[] 的保证以及上面 &temp[0] 返回的地址是否是一个可用的、以空值结尾的缓冲区作为 char*。其次,非常其次,有没有更好的方法来做到这一点?

我正在使用 C++03。

【问题讨论】:

    标签: c++ stl operators stdstring c++03


    【解决方案1】:

    这仅在 C++11 中具有明确定义的行为;在以前的标准中,std::string 不保证其内部缓冲区的连续存储。

    尽管该代码在 C++11 中完全没问题,但更惯用的方法是使用 std:vector<char>,它从 C++03 开始​​就保证了连续存储:

    void ProcessString(char* str);
    
    void SomeFunction(char const* str)
    {
        // + 1 for terminating NUL
        std::vector<char> temp(str, str + std::strlen(str) + 1);
        ProcessString(&temp[0]); // or temp.data() in C++11
    }
    

    【讨论】:

    • 如果str 不为NULL,那么您可以使用strlen()+1 并避免使用push_back()
    • 我几乎可以肯定 C++11 确实保证缓冲区中包含的字符串是以空值结尾的。 c_str 返回“一个指针 p 使得 p + i == &amp;operator[](i) 对应于 [0,size()] 中的每个 i”并要求“程序不得更改存储在字符数组中的任何值。”
    • @James: c_str 被允许添加 NUL,直到 c_str 被调用,没有要求存在一个。 “程序不得改变”是对用户可以使用结果指针做什么的限制(例如,没有 const_cast 然后修改),而不是对 c_str 实现者的限制。
    • @Ben,C++11 承诺 operator[](size()) 是对 nul 字符的引用,与 c_str 无关。但是,我没有看到&amp;operator[](size()) 紧跟&amp;operator[](size()-1) 的要求,除了在c_str 的描述中。
    • @Kaiged :在 C++11 中,std::basic_string&lt;&gt;std::vector&lt;&gt; 都保证具有连续存储。在 C++03 中,只有 std::vector&lt;&gt; 有这个保证。在 C++98 中,两者都没有这种保证。问题是您使用的编译器遵循哪个标准。
    【解决方案2】:

    我创建了一个小类来面对这种问题,我已经实现了 RAII 成语。

    class DeepString
    {
            DeepString(const DeepString& other);
            DeepString& operator=(const DeepString& other);
            char* internal_; 
    
        public:
            explicit DeepString( const string& toCopy): 
                internal_(new char[toCopy.size()+1]) 
            {
                strcpy(internal_,toCopy.c_str());
            }
            ~DeepString() { delete[] internal_; }
            char* str() const { return internal_; }
            const char* c_str()  const { return internal_; }
    };
    

    您可以将其用作:

    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
    

    我调用了 DeepString 类,因为它正在创建现有字符串的深度且唯一的副本(DeepString 不可复制)。

    【讨论】:

    • 我不认为我会用它来解决我的问题,但我喜欢看看其他人如何使用诸如此类的实用程序类来解决问题。
    【解决方案3】:

    如果您需要从const char* 转到char *,为什么不使用strdup,然后在 ProcessString 完成时释放缓冲区?

    通过std::string 对我来说似乎没有必要。

    【讨论】:

    • 使用 std::string 就是这样做的。
    • 我绝对需要某种只存在于 SomeFunction 范围内的临时缓冲区。我喜欢这个想法并使用上面 ildjarn 的 std::vector。
    • strdup 不是 ISO,需要明确的 free + 异常处理。
    猜你喜欢
    • 1970-01-01
    • 2017-01-05
    • 2015-09-06
    • 2011-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    相关资源
    最近更新 更多