Указывает ли «&s[0]» на смежные символы в std::string?

StackOverflow https://stackoverflow.com/questions/1986966

  •  22-09-2019
  •  | 
  •  

Вопрос

Я выполняю некоторые работы по техническому обслуживанию и наткнулся на что-то вроде следующего:

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?

Это было полезно?

Решение

В соответствии со стандартом C++98/03 не гарантируется, что выделение std::string будет непрерывным, но C++11 требует этого.На практике ни я, ни Херб Саттер знать реализацию, которая не использует непрерывное хранилище.

Обратите внимание, что &s[0] В соответствии со стандартом C++11 вещь всегда гарантированно работает, даже в случае строки с нулевой длиной.Это не было бы гарантировано, если бы вы это сделали str.begin() или &*str.begin(), но для &s[0] стандарт определяет operator[] как:

Возврат: *(begin() + pos) если pos < size(), в противном случае ссылка на объект типа T со стоимостью charT();указанное значение не должно быть изменено

Продолжая, data() определяется как:

Возврат: Указатель p такой, что p + i == &operator[](i) для каждого i в [0,size()].

(обратите внимание на квадратные скобки на обоих концах диапазона)


Уведомление:предварительная стандартизация C++0x не гарантировала &s[0] работать со строками нулевой длины (на самом деле это было явно неопределенное поведение), и это объяснялось в более старой версии этого ответа;это было исправлено в более поздних версиях стандартов, поэтому ответ был соответствующим образом обновлен.

Другие советы

Технически нет, поскольку std::string не требуется хранить его содержимое в памяти непрерывно.

Однако почти во всех реализациях (каждая из которых мне известна) содержимое хранится последовательно, и это «работает».

Это безопасно использовать.Я думаю, что большинство ответов когда-то были правильными, но стандарт изменился.Цитата из стандарта С++ 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 ().

Чуть раньше там говорилось, что все итераторы являются итераторами произвольного доступа.Оба бита поддерживают использование вашего вопроса.(Кроме того, Страуструп, очевидно, использует его в своей новейшей книге ;))

Не исключено, что это изменение было сделано в C++11.Кажется, я помню, что такая же гарантия была добавлена ​​тогда для вектора, который тоже получил весьма полезную информацию. данные() указатель с этим выпуском.

Надеюсь, это поможет.

Читателям следует отметить, что этот вопрос был задан в 2009 году, когда текущей публикацией был стандарт C++03.Этот ответ основан на той версии Стандарта, в которой 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 );

?

Код может работать, но скорее по счастливой случайности, чем по суждению, он делает предположения о реализации, которые не гарантируются.Я считаю, что определение валидности кода не имеет значения, поскольку это бессмысленное усложнение, которое легко сводится к следующему:

std::string s( str ) ;

или при назначении существующему объекту std::string просто:

s = str ;

а затем пусть std::string сам определит, как добиться результата.Если вы собираетесь прибегнуть к подобной ерунде, то вы можете также не использовать std::string и придерживаться его, поскольку вы вновь представляете все опасности, связанные со строками C.

Это вообще нет безопасно, независимо от того, хранится ли внутренняя последовательность строк в памяти постоянно или нет.Может быть много других деталей реализации, связанных с тем, как контролируемая последовательность хранится std::string объект, помимо непрерывности.

Реальная практическая проблема с этим может заключаться в следующем.Контролируемая последовательность std::string не требуется хранить в виде строки с нулевым завершением.Однако на практике многие (большинство?) реализаций предпочитают увеличивать размер внутреннего буфера на 1 и в любом случае сохранять последовательность как строку с нулевым завершением, поскольку это упрощает реализацию c_str() метод:просто верните указатель на внутренний буфер, и все готово.

Код, который вы указали в своем вопросе, не предпринимает никаких усилий для завершения копирования данных во внутренний буфер.Вполне возможно, что он просто не знает, необходимо ли нулевое завершение для данной реализации std::string.Вполне возможно, что это связано с заполнением внутреннего буфера нулями после вызова resize, поэтому дополнительный символ, выделенный реализацией для нулевого терминатора, удобно предварительно установить на ноль.Все это детали реализации, а это означает, что данный метод зависит от некоторых довольно хрупких предположений.

Другими словами, в некоторых реализациях вам, вероятно, придется использовать strcpy, нет memcpy чтобы принудительно привести данные в контролируемую последовательность вот так.Хотя в некоторых других реализациях вам придется использовать memcpy и не strcpy.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top