Строковые литералы внутри функций: автоматические переменные или расположены в куче? - PullRequest
3 голосов
/ 06 октября 2008

Являются ли строковые литералы, которые мы используем внутри функций автоматическими переменными? Или они размещены в куче, которую мы должны освободить вручную?

У меня есть ситуация, подобная приведенному ниже коду, в которой я присваиваю строковый литерал частному полю класса (помечен как ОДИН в коде) и получаю его намного позже в моей программе и использую его (помеченный как ДВА). Присваиваю ли я переменную в стеке полю в ОДНОМ? Может ли код ссылаться на висячий указатель, который в этом случае работал, потому что программа была достаточно маленькой?

Я скомпилировал и запустил его, он работал нормально, но у меня возникла странная ошибка в моей реальной программе, когда я назначал строковые литералы для полей класса, как это, и я подозреваю, что случай, который я упоминал выше. 1005 *

#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" как массив символов вместо указателя? Я планирую использовать std :: string, но мне просто интересно это.

Любые указатели или помощь, как всегда, высоко ценится :) Спасибо.

Ответы [ 5 ]

9 голосов
/ 06 октября 2008

Строковые литералы будут размещаться компилятором в инициализированных сегментах данных или текста (кода) вашего двоичного файла, а не в памяти (выделенной во время выполнения) или стеке. Поэтому вы должны использовать указатель, поскольку вы будете ссылаться на строковый литерал, который компилятор уже создал для вас. Обратите внимание, что изменение этого (которое обычно требует изменения защиты памяти) изменит все варианты использования этого литерала.

6 голосов
/ 06 октября 2008

Это неопределенное поведение для изменения строкового литерала, и, скорее всего, причина сбоя в вашей программе (ISO C ++: 2.13.4 / 2). Стандарт допускает преобразование строкового литерала в char* для обратной совместимости с C, и вы должны иметь это преобразование в своем коде, только если оно вам абсолютно необходимо.

Если вы хотите рассматривать строковый литерал как константу, тогда вы можете изменить тип вашего члена на const char *.

Если ваш дизайн требует изменения s, я бы рекомендовал изменить его тип на std::string.

1 голос
/ 06 октября 2008

Спасибо, Коди и Ричард.

Я нашел причину ошибки. Это было потому, что я делал удаление объекта, который уже был удален. Я делал:

if (obj != NULL) delete obj;

Я изменил его на:

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

Обучение C ++ определенно весело :) 1009 *

0 голосов
/ 06 октября 2008

Давайте посмотрим на ваши варианты.
Есть также пара вещей, которые вы должны сделать:

    /*
     * 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.
0 голосов
/ 06 октября 2008

Может быть, причина сбоя в том, что вы не завершили строку в 0?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...