2

Cなどから使用できる必要があるdllがあるため、文字列オブジェクトなどを通常のように使用することはできませんが、これを安全に行う方法がわかりません..

const char *GetString()
{
    std::stringstream ss;
    ss << "The random number is: " << rand();
    return ss.str().c_str();
}

ss がスタックから落ちたときに c 文字列が破壊される可能性はありますか? 私はそう仮定しています...

別のオプションとして、ヒープに新しい文字列を作成することもできますが、それを解放するにはどうすればよいでしょうか?

const char *GetString()
{
    std::stringstream ss;
    ss << "The random number is: " << rand();
    char *out = new char[ss.str().size()];
    strcpy(ss.str().c_str(), out);
    return out;//is out ever deleted?
}

文字列だけでなく、他のものへのポインタについても同じことが言えます。

4

10 に答える 10

8

最初のバリアントは、破棄されるスタック オブジェクトにポインターを返すため、機能しません。(より正確には、削除されたヒープメモリへのポインタを返します()。)さらに悪いことに、誰もメモリを上書きしていない場合、デバッグが非常に困難になるため、しばらくの間は機能することさえあります。

次に、次のように静的文字列へのポインターを返さない限り、const char* を返すことはできません。

const char *GetString()
{
    return "a static string in DATA segment - no need to delete";
}

2 番目のバリアントには、new() で割り当てられたメモリを、free() を呼び出す C プログラムに返すという問題があります。それらは互換性がない場合があります。

文字列を C に返す場合、2 つの方法があります。

char *GetString()
{
    std::stringstream ss;
    ss << "The random number is: " << rand();
    return strdup( ss.str().c_str() ); // allocated in C style with malloc()
}

void foo()
{
    char *p = GetString();
    printf("string: %s", p));
    free( p ); // must not forget to free(), must not use delete()
}

また:

char *GetString(char *buffer, size_t len)
{
    std::stringstream ss;
    ss << "The random number is: " << rand();
    return strncpy(buffer, ss.str().c_str(), len); // caller allocates memory
}

void foo()
{
    char buffer[ 100 ];
    printf("string: %s", GetString(buffer, sizeof( buffer ))); // no memory leaks
}

メモリ処理ポリシーに応じて。

原則として、C++ の自動オブジェクトへのポインターまたは参照を返すことはできません。これは、多くの C++ 書籍で分析されているよくある間違いの 1 つです。

于 2008-11-12T08:32:48.500 に答える
3

何年にもわたって、C はこれを 2 つの標準的な方法に要約しました。

  • 呼び出し元はバッファーに渡します。
    これには 3 つのバージョンがあります。
    バージョン 1: バッファーと長さを渡します。
    バージョン 2: ドキュメントには、予想される最小バッファー サイズが指定されています。
    バージョン 3: 飛行前。関数は、必要な最小バッファーを返します。呼び出し元は、NULL バッファーを使用して最初に 2 回呼び出します。
    • 例: read()
  • 次の呼び出しまで有効な静的バッファーを使用します。
    • 例: tmpname()

いくつかの非標準のものは、明示的に解放しなければならなかったメモリを返しました

  • strdup() が思い浮かびます。
    一般的な拡張子ですが、実際には標準にはありません。
于 2008-11-12T12:08:22.340 に答える
1

文字列ストリームは破壊時にそのスペースの割り当てを解除するため、最初のものは実際には機能しません。したがって、そのポインターを逆参照しようとすると、プログラムがクラッシュする可能性が高くなります。

あなたが言及する2番目のオプションは、通常の方法であり、関数のユーザーはスペースの割り当てを解除する必要があります。これが関数を使用する C プログラムの場合は、必ず malloc() で割り当て、free() で解放してください。

もう 1 つのオプションは、静的な char 配列のアドレスを返すことです。これは、長さの適切な上限が事前にわかっている場合に関係します。さらに重要なことに、静的配列を使用すると本質的に関数が再入不可になるため、関数が 2 つの異なるスレッドから同時に呼び出される可能性がない場合にのみ、これを使用する必要があります。

于 2008-11-12T08:28:14.733 に答える
1

当然のことながら、ガベージ コレクションを使用していない限り、関数内で割り当てられたメモリへのポインターを返すときはいつでも、割り当て解除は外部から行う必要があります。これを行いたくない場合は、GetString() を呼び出す前に文字バッファーを割り当て、プロトタイプを次のように変更します。

int get_string(const char* バッファ);

次に、バッファをいっぱいにします。ただし、malloced データにポイントを返すことは問題ありません。

于 2008-11-12T08:29:43.857 に答える
0

スレッドセーフが重要でない場合、

const char *GetString()
{
    static char *out;
    std::stringstream ss;
    ss << "The random number is: " << rand();
    delete[] out;
    char *out = new char[ss.str().size()];
    strcpy(ss.str().c_str(), out);
    return out;//is out ever deleted?
}

その後、関数は文字列の割り当てを解除する責任を引き継ぐことができます。

スレッドセーフが重要な場合、

次に、次のように引数として渡すのが最善の方法です。

void GetString(char *out, int maxlen);

これは、古い非スレッドセーフ API がスレッドセーフに変更されたときに起こることです。

于 2008-11-12T09:16:27.633 に答える
0

関数が呼び出された後、呼び出し元に文字列のメモリ (特に割り当て解除) の責任を負わせることができます。静的変数を使用したい場合を除きますが、ドラゴンがいます! これをきれいに行う最善の方法は、最初に呼び出し元にメモリの割り当てを行わせることです。

void foo() {
  char result[64];
  GetString(result, sizeof(result));
  puts(result);
}

GetString は次のようになります。

int GetString(char * dst, size_t len) {
  std::stringstream ss;
  ss << "The random number is: " << rand();
  strncpy(ss.str().c_str(), dst, len);
}

最大バッファ長を渡して strncpy() を使用すると、誤ってバッファを上書きすることを回避できます。

于 2008-11-12T09:28:13.740 に答える
0

これまでの回答は、非常に重要な問題、つまり、結果に必要なバッファーの長さが不明であり、同じ引数 (データベースからの値の読み取りなど) を使用しても、呼び出し間で変化する可能性がある場合の対処方法には対応していません。 、そのため、この状況に対処するための最善の方法と思われるものを提供しています。

サイズが事前にわからない場合は、関数にコールバック関数を渡すことを検討してください。関数はconst char*パラメーターとしてを受け取ります。

typedef void (*ResultCallback)( void* context, const char* result );

void Foo( ResultCallback resultCallback, void* context )
{
     std::string s = "....";
     resultCallback( context, s.c_str() );
}

の実装はResultCallback、必要なメモリを割り当て、 が指すバッファをコピーできますresult。私はCを想定しているので、void*明示的にキャストしたりキャストしたりしていません。

void UserCallback( void* context, const char* result )
{
    char** copied = context;
    *copied = malloc( strlen(result)+1 );
    strcpy( *copied, result );
}

void User()
{
    char* result = NULL;

    Foo( UserCallback, &result );

    // Use result...
    if( result != NULL )
        printf("%s", result);

    free( result );
}

これは最も移植性の高いソリューションであり、返される文字列のサイズを事前に知ることができない最も困難なケースにも対応します。

于 2014-06-03T19:56:01.293 に答える
0

ss を static として宣言すると、問題を回避できます。プログラムがシングルスレッド環境で実行される場合、これは適切な解決策になる可能性があります。

于 2008-11-12T08:30:06.213 に答える
0

安全に文字列を返したい場合は、ヒープに文字列を割り当てる必要があります。また、C 関数を記述するときに malloc() iso new() で割り当てる必要があります。

ポインターを返すとき (そして、C++ とは異なり、C では多くの場合、実際の選択がありません)、割り当て解除は常に懸念事項です。本当に決定的な解決策はありません。

これを処理する 1 つの方法は、かなりの数の API で見たもので、すべての関数を呼び出すことです。

CreateString()

呼び出し元がメモリの割り当てを解除する必要がある場合、および

GetString()

それが問題ではないとき。

もちろん、これは絶対確実というわけではありませんが、十分な規律があれば、正直に言うと、これが私が見た中で最良の方法です...

于 2008-11-12T08:30:28.413 に答える