私はいくつかのメンテナンス作業を行っていて、次のようなものに遭遇しました:
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の安全な使用ですか?
std :: stringの割り当ては、C ++ 98/03標準では連続していることが保証されていませんが、C++11では強制的に連続しています。実際には、私もHerb Sutterも、連続したストレージを使用しない実装を知りません。
&s[0]
長さが0の文字列の場合でも、C++11標準で常に機能することが保証されていることに注意してください。str.begin()
またはを実行したかどうかは保証されませんが&*str.begin()
、&s[0]
標準では次のように定義operator[]
されています。
戻り値:
*(begin() + pos)
if 、それ以外の場合は値を持つpos < size()
タイプのオブジェクトへの参照; 参照値は変更しないでくださいT
charT()
続行すると、data()
次のように定義されます。
戻り値:のそれぞれについての
p
ようなポインタ。p + i == &operator[](i)
i
[0,size()]
(範囲の両端にある角かっこに注意してください)
注意:事前標準化C ++ 0xは&s[0]
、長さがゼロの文字列での動作を保証していませんでした(実際には、明示的に未定義の動作でした)。この回答の古いリビジョンはこれを説明しています。これは後の標準ドラフトで修正されたため、それに応じて回答が更新されました。
技術的には、std::string
その内容をメモリに連続して保存する必要がないため、いいえ。
ただし、ほとんどすべての実装(私が知っているすべての実装)では、コンテンツは連続して保存され、これは「機能」します。
安全に使用できます。ほとんどの答えは一度正しかったと思いますが、基準が変わりました。C ++ 11標準からの引用、basic_stringの一般的な要件[string.require]、21.4.1.5は、次のように述べています。
basic_stringオブジェクトのcharのようなオブジェクトは、連続して格納されます。つまり、basic_stringオブジェクトsの場合、アイデンティティ&*(s.begin()+ n)==&* s.begin()+ nは、0 <=n<s.sizeとなるnのすべての値に対して保持されます。 ()。
その少し前に、すべてのイテレータはランダムアクセスイテレータであると書かれています。両方のビットが質問の使用法をサポートします。(さらに、Stroustrupは明らかに彼の最新の本でそれを使用しています;))
この変更がC++11で行われた可能性はほとんどありません。そのとき、同じ保証がvectorに追加されたことを覚えているようです。これは、そのリリースで非常に便利なdata()ポインターも取得しました。
お役に立てば幸いです。
読者は、この質問がC++03標準が現在の出版物であった2009年に尋ねられたことに注意する必要があります。この回答は、そのバージョンの標準に基づいており、連続したストレージを使用することが保証され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
連続性に加えて、制御されたシーケンスがオブジェクトによってどのように格納されるかに関連する他の多くの実装の詳細があるかもしれません。
それに関する実際の実際的な問題は次のようになります。の制御されたシーケンスはstd::string
、ゼロで終了する文字列として格納する必要はありません。ただし、実際には、多くの(ほとんどの?)実装は、メソッドの実装を簡素化するため、内部バッファーのサイズを1だけ大きくし、シーケンスをゼロで終了する文字列として格納することを選択します。内部バッファーc_str()
へのポインターを返すだけで完了です。 。
質問で引用したコードは、データが内部バッファーにコピーされることをゼロ終了するための努力をしていません。おそらく、この実装にゼロ終端が必要かどうかがわからないだけですstd::string
。おそらく、の呼び出し後に内部バッファがゼロで満たされることに依存しているresize
ため、実装によってゼロターミネータに割り当てられた追加の文字は、便利なことにゼロに事前設定されています。これはすべて実装の詳細です。つまり、この手法はかなり脆弱な仮定に依存しています。
言い換えると、一部の実装では、データをそのような制御されたシーケンスに強制するのstrcpy
ではなく、おそらくを使用する必要があります。memcpy
他のいくつかの実装では、使用する必要があり、使用する必要はmemcpy
ありませんstrcpy
。
コードは機能するかもしれませんが、運が良ければ、それは保証されていない実装についての仮定をします。コードの有効性を判断することは重要ではありませんが、複雑さを超えて無意味であり、簡単に次のようになります。
std::string s( str ) ;
または、既存のstd :: stringオブジェクトに割り当てる場合は、次のようにします。
s = str ;
次に、std::string自体が結果を達成する方法を決定します。この種のナンセンスに頼る場合は、C文字列に関連するすべての危険性を再導入しているため、std::stringを使用せずに固執することをお勧めします。