"& 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.