3

関数内で使用する文字列リテラルは自動変数ですか? それとも、手動で解放する必要があるヒープに割り当てられていますか?

以下に示すコードのような状況があり、文字列リテラルをクラスのプライベート フィールド (コードで ONE としてマーク) に割り当て、それを後でプログラムで取得して使用します (TWO としてマーク)。スタック内の変数を ONE 内のフィールドに割り当てていますか? この場合、プログラムが十分に小さかったために機能したダングリング ポインターをコードが参照している可能性はありますか?

コンパイルして実行しましたが、問題なく動作しましたが、実際のプログラムでこのようなクラスのフィールドに文字列リテラルを割り当てているときに奇妙なクラッシュが発生し、上記のケースが疑われます。

#include <iostream>

using namespace std;

class MemoryLeak
{
private:
    char *s;
public:
    MemoryLeak() {}

    void store()
    {
        s = "Storing a string"; // ONE
    }

    char *retrieve()
    {
        return s;
    }
};

int main()
{
    MemoryLeak *obj = new MemoryLeak();
    obj->store();
    cout << obj->retrieve() << endl; // TWO
    delete obj;
    return 0;
}

変数「s」をポインターではなくchar配列として宣言する必要がありますか? std::string を使おうと思っているのですが、ちょっと気になるところです。

いつものように、ポインタやヘルプは大歓迎です:)ありがとう。

4

5 に答える 5

9

文字列リテラルは、(実行時に割り当てられた) メモリまたはスタックに存在するのではなく、コンパイラによってバイナリの初期化されたデータまたはテキスト (コード) セグメントに配置されます。したがって、コンパイラーが既に生成した文字列リテラルを参照することになるため、ポインターを使用する必要があります。これを変更すると (通常はメモリ保護を変更する必要があります)、このリテラルのすべての使用が変更されることに注意してください。

于 2008-10-06T09:31:29.937 に答える
6

文字列リテラルを変更することは未定義の動作であり、プログラムのクラッシュの原因である可能性が最も高いです (ISO C++: 2.13.4/2)。標準では、文字列リテラルからchar*C への下位互換性のために変換が許可されており、絶対に必要な場合にのみ、コードでその変換を行う必要があります。

文字列リテラルを定数として扱いたい場合は、メンバーの型をconst char *.

デザインをs変更できる必要がある場合は、タイプを に変更することをお勧めしstd::stringます。

于 2008-10-06T09:49:58.167 に答える
1

コーディとリチャードに感謝します。

バグの原因を見つけました。すでに削除されたオブジェクトを削除していたためです。やっていた:

if (obj != NULL) delete obj;

私はそれを次のように変更しました:

if (obj != NULL)
{
    delete obj;
    obj = NULL;
}

C++を学ぶことは間違いなく楽しいです:)

于 2008-10-06T10:30:07.383 に答える
0

オプションを見てみましょう。
あなたがしなければならないことがいくつかあります:

    /*
     * Should initialize s to NULL or a valid string in constructor */
        MemoryLeak()
        {
            store();
        }

        void store()
        {
            // This does not need to be freed because it is a string literal
            // generated by the compiler.
            s = "Storing a string"; // ONE

            // Note this is allowed for backward compatibility but the string is
            // really stored as a const char* and thus unmodifiable. If somebody
            // retrieves this C-String and tries to change any of the contents the
            // code could potentially crash as this is UNDEFINED Behavior.

            // The following does need to be free'd.
            // But given the type of s is char* this is more correct.
            s = strdup("Storing a string");

            // This makes a copy of the string on the heap.
            // Because you allocated the memory it is modifiable by anybody
            // retrieving it but you also need to explicitly de-allocate it
            // with free()
        }

あなたがしていることは、C-Strings を使用することです。これらを C++ std::string と混同しないでください。C++ std::string は、空の文字列に自動初期化されます。割り当てられたメモリは、正しく割り当て解除されます。変更可能なバージョンと変更不可能なバージョンの両方として簡単に返すことができます。また、操作も簡単です (つまり、拡大縮小の変更)。C-String を拡張する場合は、メモリを再割り当てし、文字列を新しいメモリにコピーする必要があります (エラーが発生しやすく、非常に時間がかかります)。

オブジェクトの動的割り当てに対処するために、スマート ポインターについて学びます。
スマート ポインターの詳細については、この記事を参照してください。
スマートポインターまたはあなたの所有者は誰ですか

std::auto_ptr<MemoryLeak> obj(new MemoryLeak());

obj->store();
std::cout << obj->retrieve() << std::endl; // TWO

// No need to delete When object goes out of scope it auto deletes the memory.
于 2008-10-06T16:20:25.023 に答える
0

おそらくクラッシュの原因は、文字列を 0 で終了しなかったことでしょうか?

于 2008-10-06T09:48:23.177 に答える