Указывает ли адрес результата std::string::operator[] на доступный для записи буфер с завершающим нулем?

Я изменяю функцию, которая принимает 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*. Во-вторых, и во-вторых, есть ли лучший способ сделать это?

Я использую С++03.


person Kaiged    schedule 15.03.2012    source источник


Ответы (3)


Это имеет четко определенное поведение только в С++ 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
}
person ildjarn    schedule 15.03.2012
comment
Если str не равно NULL, вы можете использовать strlen()+1 и избегать push_back(). - person Remy Lebeau; 16.03.2012
comment
Я почти уверен, что С++ 11 гарантирует, что строка, содержащаяся в буфере, завершается нулем. c_str возвращает указатель p такой, что p + i == &operator[](i) для каждого i в [0,size()], и требует, чтобы программа не изменяла никакие значения, хранящиеся в массиве символов. - person James McNellis; 16.03.2012
comment
@James: c_str разрешено добавлять NUL, пока не будет вызван c_str, его существование не требуется. программа не должна изменяться — это ограничение на то, что пользователь может делать с результирующим указателем (например, нет const_cast, а затем модификация), а не ограничение на c_str разработчиков. - person Ben Voigt; 16.03.2012
comment
@BenVoigt: К сожалению, я неправильно понял, что программа не должна, так как реализация не должна. Спасибо. В любом случае, я думаю, что лучшим решением будет вызвать c_str(), а затем использовать &s[0]. - person James McNellis; 16.03.2012
comment
@Remy: Не похоже, что NULL является допустимым вводом для strlen для начала. - person Ben Voigt; 16.03.2012
comment
@Ben, С++ 11 обещает, что operator[](size()) является ссылкой на нулевой символ, независимо от c_str. Однако я не вижу требования, чтобы &operator[](size()) сразу же следовало за &operator[](size()-1), за исключением описания c_str. - person Rob Kennedy; 16.03.2012
comment
Я видел много решений моей проблемы, включая этот ответ, но пока это единственный ответ на мой реальный вопрос. Из того, что я понял, operator[] возвращает только ссылку на char, точка; никаких гарантий относительно непрерывной памяти и т. д. Это правильно? - person Kaiged; 16.03.2012
comment
@Kaiged: в С++ 11 и std::basic_string<>, и std::vector<> гарантированно имеют непрерывное хранилище. В C++03 такая гарантия была только у std::vector<>. В C++98 ни у одного из них не было такой гарантии. Вопрос в том, какого стандарта придерживается используемый вами компилятор. - person ildjarn; 16.03.2012
comment
@BenVoigt: вот почему я сказал: если str не NULL ... - person Remy Lebeau; 16.03.2012
comment
@Remy: ... и если str равно NULL, то код уже сломан, поэтому его изменение ничего не повредит. - person Ben Voigt; 16.03.2012
comment
@ildjarn: Спасибо за разъяснение. Я должен был указать, что наш компилятор использует C++03. Жаль, что какое-то время мы не будем обновляться до C++11. - person Kaiged; 16.03.2012
comment
@RobKennedy: Спасибо за этот комментарий. Я был уверен, что это требование было где-то там, я просто не мог его найти. - person James McNellis; 16.03.2012

Я создал небольшой класс для решения такого рода проблем, я реализовал идиому 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 не может быть скопирован) существующей строки.

person Alessandro Teruzzi    schedule 15.03.2012
comment
Я не думаю, что буду использовать это для решения своей проблемы, но мне нравится смотреть, как другие решают проблемы с такими служебными классами. - person Kaiged; 16.03.2012

Если вам нужно перейти с const char* на char *, почему бы не использовать strdup, а затем освободить буфер, когда ProcessString завершится?

Прохождение std::string кажется мне ненужным.

person David Yaw    schedule 15.03.2012
comment
Использование std::string делает именно это. - person Remy Lebeau; 16.03.2012
comment
Мне определенно нужен какой-то временный буфер, который существует только в рамках SomeFunction. Мне нравится эта идея и использование std::vector от ildjarn выше. - person Kaiged; 16.03.2012
comment
strdup не является ISO и требует явной обработки исключений free +. - person Fred Foo; 16.03.2012