2017-08-09 4 views
7

私は、次のコード(簡体字)を見て、それがこのreturnMsg機能を使用する方法も安全です自問しています:内部的に静的なstd :: stringを使用する関数からconst char *を返すのは安全ですか?

#include <iostream> 

using namespace std; 

const char *returnMsg(const char *msg) 
{ 
    static std::string message; 

    message = msg; 

    return message.c_str(); 
} 

int main(int argc, char *argv[]) 
{ 
    const char *msg1 = returnMsg("Hello world"); 
    printf("msg1 = %p\n", msg1); 
    cout << msg1 << endl; 

    const char *msg2 = returnMsg("Good bye"); 
    printf("msg2 = %p\n", msg2); 
    cout << msg2 << endl; 

    cout << msg1 << endl; 

    return 0; 
} 

出力は次のとおりです。

msg1 = 0x23a6028 
Hello world 
msg2 = 0x23a6028 
Good bye 
Good bye 

msg2は二回書かれていると、それは何ですか私は静的メッセージ変数はプログラムの寿命中にメモリに残っており、メモリの再割り当てがないので、msg1のアドレスに書き込まれた内容は新しい内容のmsg2に置き換えられます。 msg2のサイズが大きい場合

しかし、std::string message変数内の内部再配分があり、出力は次のとおりです。

msg1 = 0x1cc6028 
Hello world 
msg2 = 0x1cc6058 
Good bye looooooooooooooooooooooooooooooooooooooooong 
Hello world 

が、私はmsg1アドレスはで再利用されないという保証がないことを推測します将来的には、msg1コンテンツに新たにアクセスすると、最終的には異なるものとなり、一貫性のないものが表示される可能性があります。

上記の制限なしにこの機能を使用できるようにするには、この機能を別の方法で書き込む必要がありますか?

+0

実際に行うはずの機能は何ですか? 'const char * msg2 ="さようなら ";と'あなたはあなたが今いる問題に対処する必要はありません – user463035818

+0

@ tobi303私はOPのために話すことができませんが、静的ではない文字列で関数が呼び出され、そのバッファがもはや存在しなくなった後にポインタが使用されるところでは、わずかに少ない例を考えます。この関数は、その内容を静的な文字列にコピーすることによって、そのバッファの存続期間を便利に延長することができます。ただし、次回の関数呼び出しまでです。 – user2079303

+0

@ user2079303嫌なことはありませんが、私はそのような機能を装飾的な世界とみなしています。それはグローバルよりも悪いです。 – user463035818

答えて

9

内部的に静的なstd :: stringを使用する関数からconst char *を返すことは安全ですか?

はい、安全です。

しかし、そのポインタが無効にされた後にそのポインタを使用することは安全ではありません。これは、示されたプログラムが行うものです。再割り当てされた場合、その関数への連続呼び出しでの割り当てによってポインタが無効になります。したがって、ポインターは関数への次の呼び出し(再割り振りを引き起こす)までしか安全ではありません。

上記の制限なしにこの機能を使用できるようにするには、この機能を別の方法で書き込む必要がありますか?

この機能には説明されている制限がありますので、これらの制限がないように別の方法で記述する必要があります。

あなたのアプローチの中核的な問題は、静的な文字列を1つだけ持つことですが、複数の文字列を保存して以前のものを捨てないことです。だから、静的な文字列がたくさんあるようです:

const char *returnMsg(const char *msg) 
{ 
    static std::forward_list<std::string> messages; 
    messages.emplace_front(msg); 
    return messages.front().c_str(); 
} 

期待通りに機能しますが、それは愚かです。残りの実行のためにすべての文字列を実際に保存するかどうかを検討してください。そうでない場合、静的ストレージは解決策ではありません。

+0

NB。ポインタは無効になることができます。つまり、割り当て中に再割り当てが必要な場合です。そうでないときは安全です(あまり賢明ではありませんが)。 –

+0

@ engf-010それを考慮に入れて資格を付けました。 – user2079303

3

上記のコードはになります。未定義の動作。ちょうどc_str()ドキュメントを見て:

c_str()から得られたポインタをすることによって無効にすることができる。

  • 任意の標準ライブラリ関数に文字列への非const参照を渡す、または
  • 演算子[]、at()、front()、back()、begin()、rbegin()、end()および rend()を除く文字列で非constメンバー関数を呼び出します。

あなたはreturnMsg()内で二度目のoperator=を呼び出し、msg1は無効とそれが未定義の動作で使用されます。

+0

NB。ポインタは無効になることができます。つまり、割り当て中に再割り当てが必要な場合です。そうでないときは安全です(あまり賢明ではありませんが)。 –

関連する問題