Строковые литералы внутри функций: автоматические переменные или выделенные в куче?

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

У меня есть ситуация, подобная приведенному ниже коду, в котором я назначаю строковый литерал частному полю класса (помеченному как ОДИН в коде) и получаю его намного позже в моей программе и использую его (помечено как ДВА). Назначаю ли я переменную в стеке полю в 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" как массив символов вместо указателя? Я планирую использовать std :: string, но мне это просто интересно.

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
2 584
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

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

Его изменение незаконно. Это действительно const char *.

Martin York 06.10.2008 20:01

+1 к const char *. MemoryLeak :: s должен быть const и должен быть const char * MemoryLeak :: retrieve ()

quamrana 06.10.2008 21:20

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

Эээ, строковые литералы всегда заканчиваются нулем.

Serafina Brocious 06.10.2008 13:49

Зависит от компилятора, не так ли?

Treb 06.10.2008 13:54

Нет, если вы не имеете дело с компилятором 30-летней давности: P

Serafina Brocious 06.10.2008 13:55

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

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

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

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

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

if (obj != NULL) delete obj;

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

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

Изучение C++ определенно весело :)

Да - это весело! Что касается обнаруженной вами ошибки, я бы действительно рекомендовал вам использовать интеллектуальные указатели. Выполните поиск по запросам «умный указатель» и «RAII» в StackOverflow, и вы найдете полезную информацию. Умные указатели позаботятся об управлении временем жизни объекта за вас.

Richard Corden 06.10.2008 14:36

удаление должно выполняться в деструкторах. Возможно, в назначении, но даже там copy-to-temp-and-swap обычно безопаснее (деструктор временного файла очищает замененное старое значение)

MSalters 06.10.2008 18:29

@ Ричард: Я посмотрю на умные указатели и RAII. @ onebyone.livejournal.com: Спасибо за это. Я удалю проверки NULL перед удалением в моем коде. Но, как заметил Ричард, умные указатели кажутся лучшим выбором, но не раньше, чем я узнаю об этом :) @MSalters: Я не уверен, что понимаю вас. :(

Srikanth 06.10.2008 19:02

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

    /*
     * 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.

Другие вопросы по теме