c++ - memcat - memmove
¿"& S[0]" apunta a caracteres contiguos en std:: string? (6)
El código podría funcionar, pero más por la suerte que por el juicio, hace suposiciones sobre la implementación que no están garantizadas. Sugiero que determinar la validez del código es irrelevante mientras que es una complicación sin sentido que se reduce fácilmente a solo:
std::string s( str ) ;
o si se asigna a un objeto std :: string existente, simplemente:
s = str ;
y luego deje que std :: string determine cómo lograr el resultado. Si va a recurrir a este tipo de tonterías, entonces bien podría no estar usando std :: string y atenerse a ellas ya que está reintroduciendo todos los peligros asociados con las cadenas C.
Estoy haciendo un trabajo de mantenimiento y encontré algo como lo siguiente:
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é que usar & s [0] sería seguro si fuera un std :: vector, pero ¿es esto un uso seguro de std :: string?
Es seguro de usar Creo que la mayoría de las respuestas fueron correctas una vez, pero el estándar cambió. Citando el estándar C ++ 11, basic_string requisitos generales [string.require] , 21.4.1.5, dice:
Los objetos tipo char en un objeto basic_string se deben almacenar contiguamente. Es decir, para cualquier objeto basic_string s, la identidad & * (s.begin () + n) == & * s.begin () + n se mantendrá para todos los valores de n tales que 0 <= n <s.size ().
Un poco antes de eso, dice que todos los iteradores son iteradores de acceso aleatorio. Ambos bits admiten el uso de su pregunta. (Además, Stroustrup aparentemente lo usa en su último libro;))
No es improbable que este cambio se haya realizado en C ++ 11. Me parece recordar que se agregó la misma garantía para vector, que también obtuvo el puntero de datos () muy útil con esa versión.
Espero que ayude.
Esto generalmente no es seguro, independientemente de si la secuencia interna de la secuencia se almacena continuamente en la memoria o no. Puede haber muchos otros detalles de implementación relacionados con cómo la secuencia controlada es almacenada por el objeto std::string
, además de la continuidad.
Un problema práctico real con eso podría ser el siguiente. No se requiere que la secuencia controlada de std::string
se almacene como una cadena terminada en cero. Sin embargo, en la práctica, muchas implementaciones (¿la mayoría?) Eligen sobredimensionar el búfer interno en 1 y almacenar la secuencia como una cadena terminada en cero porque simplifica la implementación del método c_str()
: simplemente devuelva un puntero al búfer interno y estás listo.
El código que citó en su pregunta no hace ningún esfuerzo para terminar en cero los datos que se copian en el búfer interno. Es muy posible que simplemente no sepa si la terminación cero es necesaria para esta implementación de std::string
. Es muy posible que dependa de que el búfer interno se llene de ceros después de la llamada para resize
, por lo que el carácter adicional asignado para el cero-terminador por la implementación se preconfigura convenientemente a cero. Todo esto es un detalle de implementación, lo que significa que esta técnica depende de algunas suposiciones bastante frágiles.
En otras palabras, en algunas implementaciones, probablemente tendrías que usar strcpy
, no memcpy
para forzar los datos en la secuencia controlada de esa manera. Mientras que en algunas otras implementaciones tendrías que usar memcpy
y no strcpy
.
Los lectores deben tener en cuenta que esta pregunta se realizó en 2009, cuando el estándar C ++ 03 era la publicación actual. Esta respuesta se basa en esa versión del Estándar, en la cual std::string
s no garantiza la utilización del almacenamiento contiguo. Dado que esta pregunta no se formuló en el contexto de una plataforma en particular (como gcc), no hago suposiciones sobre la plataforma de OP, en particular, sobre el clima o si no utilizó el almacenamiento contiguo para la string
.
¿Legal? Tal vez tal vez no. ¿Seguro? Probablemente, pero tal vez no. Buen código? Bueno, no vayamos allí ...
Por qué no solo hacer:
std::string s = str;
...o:
std::string s(str);
...o:
std::string s;
std::copy( &str[0], &str[strLen], std::back_inserter(s));
...o:
std::string s;
s.assign( str, strLen );
?
No se garantiza que la asignación de std :: string sea contigua bajo el estándar C ++ 98/03, pero C ++ 11 lo obliga a serlo. En la práctica, ni yo ni Herb Sutter sabemos de una implementación que no usa almacenamiento contiguo.
Tenga en cuenta que siempre se garantiza que el elemento &s[0]
funciona según el estándar C ++ 11, incluso en el caso de cadena de longitud 0. No estaría garantizado si str.begin()
o &*str.begin()
, pero para &s[0]
el estándar define operator[]
como:
Devuelve :
*(begin() + pos)
sipos < size()
, de lo contrario, una referencia a un objeto de tipoT
con valorcharT()
; el valor referenciado no se modificará
Continuando, data()
se define como:
Devuelve: Un puntero
p
tal quep + i == &operator[](i)
para cadai
en[0,size()]
.
(observe los corchetes en ambos extremos del rango)
Aviso : la preestandarización C ++ 0x no garantizaba que &s[0]
funcionara con cadenas de longitud cero (en realidad, era un comportamiento explícitamente indefinido), y una revisión anterior de esta respuesta lo explicó; esto se ha corregido en borradores estándar posteriores, por lo que la respuesta se ha actualizado en consecuencia.
Técnicamente, no, ya que std::string
no es necesario para almacenar su contenido contiguamente en la memoria.
Sin embargo, en casi todas las implementaciones (cada implementación de la que tengo conocimiento), los contenidos se almacenan contiguamente y esto "funcionará".