2016-03-14 10 views
7

私はC++でCライブラリを使用しており、ラッパーを記述しています。ある時点で私はstd::stringをCスタイルの文字列に変換する必要があります。文字列を返す関数を持つクラスがあります。返された文字列をキャストすることは、文字列が短い場合は機能し、そうでない場合は機能します。ここでは簡単で、問題を示す例を削減:キャストc_str()は短い文字列に対してのみ有効

コンソールにこの印刷物上のファイルを実行すると
#include <iostream> 
#include <string> 

class StringBox { 
public: 
    std::string getString() const { return text_; } 

    StringBox(std::string text) : text_(text){}; 

private: 
    std::string text_; 
}; 

int main(int argc, char **argv) { 
    const unsigned char *castString = NULL; 
    std::string someString = "I am a loooooooooooooooooong string"; // Won't work 
    // std::string someString = "hello"; // This one works 

    StringBox box(someString); 

    castString = (const unsigned char *)box.getString().c_str(); 
    std::cout << "castString: " << castString << std::endl; 

    return 0; 
} 

castString:

私はsomeStringにコメントを入れ替える場合のに対し、正しく印刷されます

キャストストリング:hello

これはどのように可能ですか?

答えて

16

getString()メンバ関数によってリターンされた一時的な文字列オブジェクトでc_strを呼び出しています。 c_str()によって返されたポインタは、元の文字列オブジェクトが存在する限り有効です。したがって、castStringを割り当てる行の最後には、ダングリングポインタになります。正式には、これは未定義の動作につながります。

なぜ、これは短い文字列に対して機能しますか?文字列データがヒープではなく文字列オブジェクトのバイトの内部に格納されているような短い長さの最適化の効果を見ていると思います。返された一時的な文字列はスタックに格納されていた可能性があります。したがって、クリーンアップされたときには割り当てが解除されず、期限切れの文字列オブジェクトへのポインタは古い文字列バイトを保持します。これはあなたが見ているものと一貫しているようですが、あなたがやっていることが良い考えであるということを意味するわけではありません。 :-)

+0

ありがとうございました!面白いのは、私の他のシステムで数ヶ月間完全に正常に動作したコードです。 Ubuntuを14.04から15.10にアップデートし、変更されたすべてを再インストールしたばかりです。私はそれが未定義の行動だと思う... – Cat

+0

そして精巧に感謝します。あなたは「良い考えではない」という意味を説明できますか?または、関数から返されたオブジェクトに対してc_str()を呼び出すという私の間違いを指摘していますか? – Cat

+0

私は簡単な修正があると思います: 'std :: cout <<" castString: "<<(const unsigned char *)box.getString().c_str()<< std :: endl;' – chqrlie

6

box.getString()は、匿名のテンポラリです。 c_str()は、変数の長さに対してのみ有効です。

あなたのケースでは、std::coutになるまでにc_str()で無効になります。になります。ポインタの内容を読み取る動作は、は未定義です。

は(興味深いことに、あなたの短い文字列の振る舞いが原因に std::stringは異なる方法で短い文字列を格納可能性が異なる です。)

+1

私は/長さ/人生でしょう – NathanOliver

+0

助けてくれてありがとう、私は今理解しています! – Cat

5

あなたが値で返したよう

box.getString()はそう

一時的なものであり、

box.getString().c_str()は、式の間だけ有効です。それは、ぶら下がりポインタです。

あなたはと

const std::string& getString() const { return text_; } 
+0

ご協力いただきありがとうございます! – Cat

+0

これは、 'getString()'の戻り値が 'StringBox'によって保持された' _text'への参照であり(あなたのコードのような匿名の一時的なものではないので)、 ' .c_str() 'は' _text'の有効期間や '_text'の他の変更操作に対して有効です。 –

5

box.getString()が一時的に生成することを定めることができます。 c_str()を呼び出すと、一時的なものへのポインタが得られます。一時的に存在しなくなった後すぐに、ポインタは無効ですダングリングポインタ

ダングリングポインタの使用は未定義の動作です。

4

まず第一に、あなたのコードは、文字列の長さのUBから独立しています

castString = (const unsigned char *)box.getString().c_str(); 

の終わりにgetStringによって返された文字列が破壊され、castStringは、内部バッファへのダングリングポインタであります破壊された文字列オブジェクト。

小さい文字列の最適化:短い文字列の最適化:短い文字列は動的に割り当てられた配列に保存されるのではなく、文字列オブジェクト自体に保存されます。あなたの場合は変更されません。

関連する問題