9

文字列リテラルまたは関数からの文字列を返すことについて、私はいつも混乱しています。いつメモリが削除されるかわからないので、メモリリークが発生している可能性があると言われましたか?

例えば以下のコードで、foo()コードの出力が「Hello World」になるように実装するにはどうすればよいでしょうか。

void foo (       )              // you can add parameters here.
{

}

int main ()
{
    char *c;
    foo (    );
    printf ("%s",c);
    return 0;
}

また、 の戻り値の型がfoo()void ではないが、 を返すことができる場合、char*それは何ですか?

4

4 に答える 4

11

mainを変更できないと思います。プログラムをリークなしで動作させるには、静的ストレージを用意する必要があります。

void foo(char*& pC)  // reference
{
    static char theString[] = "thingadongdong";

    pC = theString;
}

しかし実際には、これはあまり一般的なC++コードではありません。std::stringとを使用するので、メモリについて心配する必要std::coutはありません

std::string foo(void)
{
    return "better thingadongdong";
}

int main(void)
{
    // memory management is done
    std::cout << foo() << std::endl;
}

何かを手動で割り当て解除する必要があるかどうか疑問に思っている場合、それは間違って行われています。

于 2010-03-20T01:34:15.723 に答える
8

char *の古い使用は非推奨になっているので、単に文字列を使用することはできませんか?

const char* func1 () {return "string literal";}

string func2 () {return "another string literal";}

これらは両方とも正常に機能し、コンパイラの警告はありません。

でも

char* func3 () {return "yet another string literal";}

まったくコンパイルされません。また、

char* func4 () {return &"a ref to a string literal?";}

Stroustrupは、「C ++プログラミング言語」(第3版)で次のように述べています。

「文字列リテラルは静的に割り当てられるため、関数から安全に返すことができます。

const char* error_message (int i)`
{
//...
return "range error";
}

error_messages()を呼び出しても、メモリ保持範囲エラーは解消されません。」

したがって、プログラム内のすべての文字列リテラルは、プログラムの期間中持続する独自の小さなメモリに割り当てられます(つまり、静的に割り当てられます)。constをchar*の前に置くと、コンパイラは、文字列リテラルの小さなメモリを変更するつもりはない(そして変更できない)ことを知らせます。そのため、文字列リテラルからchar *に変換しても、この割り当てはスライドします。非推奨です。

代わりに文字列に戻るには、文字列リテラルを文字列型のオブジェクト(呼び出し元が担当するメモリ)にコピーする必要があります。

どちらの方法でも、メモリリークは発生しません。すべての文字列リテラルは、プログラムの終了時にクリーンアップされる独自のメモリを取得します。return to const char *は、リテラルのメモリへのポインタを返します(変更できないことを知っています)。文字列に戻ると、呼び出し元のコードに存在する文字列オブジェクトにコピーが作成され、呼び出し元によってクリーンアップされます。

表記的には少し醜いように見えますが、安価な代替手段(コピーを含まない)を維持するために、constchar*を残したと思います。

于 2012-06-16T15:12:22.200 に答える
4

文字列リテラルまたは関数からの文字列を返すことについて、私はいつも混乱しています。

不変のリテラル文字列

私が理解しているように、戻り値の型が const と宣言されている場合は、文字列リテラルを直接返して、文字列が変更されることを意図していないことを宣言しても安全です。つまり、文字列の寿命やメモリ リークについて心配する必要はありません。

変更可能な非リテラル文字列

ただし、その場で変更できる文字列が必要な場合は、文字列の寿命と、文字列が格納されるメモリ割り当てのサイズを考慮する必要があります。以前の使用によってそのメモリの内容が変更されたり、まだ使用されている可能性があるため、関数の呼び出しごとに文字列を含む同じメモリを気軽に返すことができなくなるため、これは問題になります。したがって、返された文字列を保持するために、新しいメモリを割り当てる必要があります。

これは、リークが発生する可能性がある場所であり、割り当てと割り当て解除を行う場所を選択する必要がある場所です。関数自体にメモリを割り当てさせ、これが発生したことをドキュメントに記載し、メモリが不要になったときに呼び出し元がメモリを解放する必要があることを規定することができます (リークを防ぎます)。これは、関数が単純に char * を返すことができることを意味します。

もう 1 つのオプションは、呼び出し元によって割り当てられた関数に一部のメモリを渡し、関数にそのメモリ内に文字列を配置させることです。この場合、呼び出し元はそのメモリを割り当て、解放する責任があります。

最後に、変更可能な文字列を使用する場合、メモリと文字列のサイズを管理する必要があることを述べました。割り当ては、関数によって最初に設定された文字列と、関数の後、メモリが解放される前に行われた変更に対して十分な大きさである必要があります。これを正しく行わないと、最初に割り当てられたメモリに収まるように文字列を long に書き込むことにより、バッファ オーバーフローが発生する可能性があります。これは、プログラムの健全性とセキュリティにとって非常に危険です。これは、見つけるのが非常に難しいバグやセキュリティ ホールを引き起こす可能性があります (エラーの原因 - オーバーフロー - は、プログラムが失敗したときに見られる症状とはかけ離れている可能性があるため)。

于 2010-03-20T02:08:47.417 に答える
1

このようなもの:

void foo(char ** pChar)
{
    // Make sure the string is shorter
    // than the buffer
    *pChar = new char[256];
    strcpy(*pChar,"Hello World!");
}

次に、次のように呼び出します。

foo(&c);

コメントで述べたように、保存している文字列がバッファよりも小さいことに注意してください。そうしないと、スタックオーバーフローが発生します。(しゃれを意図した)

于 2010-03-20T01:37:25.430 に答える