"& S [0]" указывает на смежные символы в std::string?
Я выполняю некоторые работы по техническому обслуживанию и сталкивался с чем-то вроде следующего:
std::string s;
s.resize( strLength );
// strLength is a size_t with the length of a C string in it.
memcpy( &s[0], str, strLength );
Я знаю, что использование & s [0] было бы безопасным, если бы оно было std::vector, но безопасно ли это использовать std::string?
Ответы
Ответ 1
A std::string Не гарантируется, что распределение не будет смежным по стандарту С++ 98/03, но С++ 11 заставляет его быть. На практике ни я, ни Herb Sutter не знают о реализации, которая не использует непрерывное хранилище.
Обратите внимание, что функция &s[0]
всегда гарантируется работой по стандарту С++ 11, даже в случае строки с длиной строки 0. Это не гарантировалось бы, если вы сделали str.begin()
или &*str.begin()
, но для &s[0]
стандарт определяет operator[]
как:
Возвращает: *(begin() + pos)
если pos < size()
, в противном случае ссылка на объект типа T
со значением charT()
; ссылочное значение не должно быть изменено
Продолжая, data()
определяется как:
Возвращает: указатель p
такой, что p + i == &operator[](i)
для каждого i
в [0,size()]
.
(обратите внимание на квадратные скобки на обоих концах диапазона)
Примечание: предварительная стандартизация С++ 0x не гарантировала &s[0]
работу с нулевыми строками (на самом деле это было явно поведение undefined), и более старая ревизия этого ответа объясняла это; это было исправлено в более поздних стандартных черновиках, поэтому ответ был соответствующим образом обновлен.
Ответ 2
Технически, нет, так как std::string
не требуется хранить его содержимое в памяти.
Однако, почти во всех реализациях (каждая реализация которых я знаю) содержимое постоянно хранится, и это "работает".
Ответ 3
Безопасно использовать. Я думаю, что большинство ответов были правильными один раз, но стандарт изменился. Цитируя из стандарта С++ 11, общие требования basic_string [string.require], 21.4.1.5, говорят:
Объекты char в объекте basic_string сохраняются смежно. То есть для любой basic_string объект s, тождество & * (s.begin() + n) == & * s.begin() + n выполняется для всех значений n таких, что 0 <= n < s.size().
Немного до этого, он говорит, что все итераторы являются итераторами произвольного доступа. Оба бита поддерживают использование вашего вопроса. (Кроме того, Страуструп, по-видимому, использует его в своей новейшей книге;))
Не исключено, что это изменение было сделано на С++ 11. Кажется, я помню, что та же гарантия была добавлена тогда для вектора, который также получил очень полезный указатель data() с этим выпуском.
Надеюсь, что это поможет.
Ответ 4
Читатели должны отметить, что этот вопрос был задан в 2009 году, когда С++ 03 Standard был текущей публикацией. Этот ответ основан на этой версии Стандарта, в которой std::string
не гарантируется использование непрерывного хранилища. Поскольку этот вопрос не задавался в контексте конкретной платформы (например, gcc), я не делаю никаких предположений о платформе OP - в частности, о погоде или нет, то для string
используется условное хранилище.
Юридический? Может быть, может и нет. Безопасно? Наверное, но, возможно, нет. Хороший код? Ну, пусть не туда...
Почему бы просто не сделать:
std::string s = str;
... или:
std::string s(str);
... или
std::string s;
std::copy( &str[0], &str[strLen], std::back_inserter(s));
... или:
std::string s;
s.assign( str, strLen );
?
Ответ 5
Это, как правило, небезопасно, независимо от того, сохраняется ли внутренняя последовательность строк в памяти непрерывно или нет. Может быть много других деталей реализации, связанных с тем, как контролируемая последовательность хранится объектом std::string
, кроме непрерывности.
Реальная практическая проблема с этим может быть следующей. Управляемая последовательность std::string
не требуется хранить в виде строки с нулевым завершением. Однако на практике многие (большинство?) Реализаций выбирают, чтобы увеличить внутренний буфер на 1 и сохранить последовательность как строку с нулевым завершением, так как она упрощает реализацию метода c_str()
: просто верните указатель на внутренний буфер и вы сделали.
Код, который вы указали в своем вопросе, не прикладывает никаких усилий для нулевого завершения копирования данных во внутренний буфер. Вполне возможно, что он просто не знает, требуется ли нулевое завершение в этой реализации std::string
. Вполне возможно, что он полагается на заполнение внутренним буфером нулей после вызова resize
, поэтому дополнительный символ, выделенный для нулевого терминатора реализацией, удобно предварительно установлен на ноль. Все это детализация реализации, что означает, что этот метод зависит от некоторых довольно хрупких предположений.
Другими словами, в некоторых реализациях вам, вероятно, придется использовать strcpy
, а не memcpy
для принудительного ввода данных в управляемую последовательность. Хотя в некоторых других реализациях вам нужно будет использовать memcpy
, а не strcpy
.
Ответ 6
Код может работать, но скорее удачей, чем суждением, он делает предположения о реализации, которые не гарантируются. Я предлагаю, чтобы определение действительности кода было неуместным, в то время как это бессмысленно над усложнением, которое легко сводится к простому:
std::string s( str ) ;
или при назначении существующего объекта std::string, просто:
s = str ;
а затем пусть std::string сам определяет, как достичь результата. Если вы собираетесь прибегать к подобным ерундам, то вы также можете не использовать std::string и придерживаться, поскольку вы повторно вводите все опасности, связанные с строками C.