“& S [0]” aponta para caracteres contíguos em uma string std ::?
Pergunta
Estou fazendo algum trabalho de manutenção e encontrei algo como o seguinte:
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 );
Eu sei que o uso de & s [0] seria seguro se fosse um vetor de std ::, mas isso é um uso seguro de STD :: string?
Solução
A alocação de STD :: String não é garantida como contígua no padrão C ++ 98/03, mas o C ++ 11 obriga a ser. Na prática, nem eu nem Erva sutter Conheça uma implementação que não usa armazenamento contíguo.
Observe que o &s[0]
É sempre garantido que o trabalho do C ++ 11, mesmo no caso de string de 0 comprimento. Não seria garantido se você fez str.begin()
ou &*str.begin()
, mas pelo &s[0]
o padrão define operator[]
Como:
Retorna:
*(begin() + pos)
E sepos < size()
, caso contrário, uma referência a um objeto do tipoT
com valorcharT()
; O valor referenciado não deve ser modificado
Continuando, data()
é definido como:
Retornos: Um ponteiro
p
de tal modo quep + i == &operator[](i)
para cadai
dentro[0,size()]
.
(Observe os suportes quadrados nas duas extremidades da linha)
Perceber: Pré-padrão C ++ 0x não garantiu &s[0]
Trabalhar com cordas de comprimento zero (na verdade, foi explicitamente indefinido comportamento), e uma revisão mais antiga dessa resposta explicou isso; Isso foi corrigido em rascunhos padrão posteriores, portanto a resposta foi atualizada de acordo.
Outras dicas
Tecnicamente, não, desde std::string
não é necessário para armazenar seu conteúdo contíguo na memória.
No entanto, em quase todas as implementações (toda implementação da qual estou ciente), o conteúdo é armazenado de forma contígua e isso "funcionaria".
É seguro usar. Eu acho que a maioria das respostas estava correta uma vez, mas o padrão mudou. Citando do padrão C ++ 11, Basic_String Requisitos gerais [String.Require, 21.4.1.5, diz:
Os objetos do tipo char em um objeto Basic_String devem ser armazenados de forma contígua. Isto é, para qualquer objeto Basic_String s, a identidade &*(s.Begin () + n) == &*s.Begin () + n deve manter todos os valores de n tal que 0 <= n <s.size ().
Um pouco antes disso, diz que todos os iteradores são iteradores de acesso aleatório. Ambos os bits apóiam o uso da sua pergunta. (Além disso, o Stroustrup aparentemente o usa em seu livro mais novo;))
Não é improvável que essa mudança tenha sido feita no C ++ 11. Parece que me lembro que a mesma garantia foi adicionada para o vetor, que também obteve o muito útil dados() Ponteiro com esse lançamento.
Espero que ajude.
Os leitores devem observar que essa pergunta foi feita em 2009, quando o padrão C ++ 03 foi a publicação atual. Esta resposta é baseada nessa versão do padrão, em que std::string
s são não garantido para utilizar armazenamento contíguo. Como essa pergunta não foi feita no contexto de uma plataforma específica (como o GCC), não faço suposições sobre a plataforma da OP - em particular, clima ou não, utilizou armazenamento contigioso para o string
.
Jurídico? Talvez talvez não. Seguro? Provavelmente, mas talvez não. Bom código? Bem, não vamos lá ...
Por que não apenas fazer:
std::string s = str;
...ou:
std::string s(str);
...ou:
std::string s;
std::copy( &str[0], &str[strLen], std::back_inserter(s));
...ou:
std::string s;
s.assign( str, strLen );
?
O código pode funcionar, mas mais por sorte do que julgamento, faz suposições sobre a implementação que não são garantidas. Sugiro determinar a validade do código é irrelevante, enquanto é uma complicação inútil que é facilmente reduzida a apenas:
std::string s( str ) ;
ou se atribuir a um objeto STD :: String existente, apenas:
s = str ;
e então deixe o próprio std :: string determinar como alcançar o resultado. Se você vai recorrer a esse tipo de bobagem, também pode não estar usando o STD :: String e siga, pois está reintroduzindo todos os perigos associados a S Strings.
Isso é geralmente não Seguro, independentemente de a sequência de string interna ser armazenada na memória continuamente ou não. Pode haver muitos outros detalhes de implementação relacionados a como a sequência controlada é armazenada por std::string
objeto, além da continuidade.
Um problema prático real com isso pode ser o seguinte. A sequência controlada de std::string
Não é necessário que seja armazenado como uma string terminada zero. No entanto, na prática, muitas (a maioria?) As implementações optam por dimensionar o buffer interno por 1 e armazenar a sequência como uma corda terminada de zero de qualquer maneira, porque simplifica a implementação de c_str()
Método: basta retornar um ponteiro ao buffer interno e você terminou.
O código que você citou em sua pergunta não faz nenhum esforço para terminação zero, os dados são copiados para o buffer interno. Muito possivelmente, simplesmente não sabe se a terminação zero é necessária para esta implementação de std::string
. Muito possivelmente, depende do buffer interno ser preenchido com zeros após a chamada para resize
, portanto, o caractere extra alocado para o terminador zero pela implementação é convenientemente predefinido para zero. Tudo isso é um detalhe de implementação, o que significa que essa técnica depende de algumas suposições bastante frágeis.
Em outras palavras, em algumas implementações, você provavelmente teria que usar strcpy
, não memcpy
Para forçar os dados na sequência controlada como essa. Enquanto estiver em outras implementações, você teria que usar memcpy
e não strcpy
.