вопросы памяти, новые и бесплатные и т. д. (C ++) - PullRequest
4 голосов
/ 04 февраля 2010

У меня есть несколько вопросов, касающихся обработки памяти в C ++.

  1. В чем разница с Mystruct *s = new Mystruct и Mystruct s? Что происходит в памяти?

  2. Глядя на этот код:

    struct MyStruct{
        int i;
        float f;
    };
    
    MyStruct *create(){
        MyStruct tmp;
        tmp.i = 1337;
        tmp.j = .5f;
        return &tmp;
    }
    
    int main(){
        MyStruct *s = create();
        cout << s->i;
    
        return 0;
    }
    

Когда MyStruct tmp free'd? Почему MyStruct tmp автоматически не освобождается в конце create()?

Спасибо!

Ответы [ 9 ]

8 голосов
/ 04 февраля 2010

Когда вы используете ключевое слово new для получения указателя, ваша структура выделяется в куче, которая гарантирует, что она будет сохраняться в течение всего времени жизни вашего приложения (или до его удаления).

Когда вы нет., структура распределяется в стеке и будет уничтожена, когда закончится область, в которой она была выделена.

Мое понимание вашего примера (пожалуйста, не стесняйтесь сообщить мне, если я ошибаюсь, кто-нибудь):

tmp действительно будет «освобожден» (не лучший выбор слова для переменной стека) в конце функции, так как она была размещена в стеке, и этот кадр стека был потерян.Адрес указателя / памяти, который вы возвращаете, больше не имеет никакого значения, и если код работает, вам просто повезло (старые данные еще ничего не перезаписали).

3 голосов
/ 04 февраля 2010

В вопросе 1 вы смотрите на кучную память и стековую память.Короче говоря,

Mystruct S;

создает S в стеке.Когда S выходит из области видимости, он будет уничтожен.Следовательно, если S находится внутри функции, когда функция возвращается, S уничтожается.

Принимая во внимание, что

MyStruct *S = new MyStruct();

Находится в куче.Это блок памяти, отведенный программам для хранения переменных, и S будет хранить указатель на начальный блок памяти нового MyStruct.Это всегда будет в куче, пока вы не освободите его;если вы не освобождаете ее, когда ваша программа заканчивается, вы получаете гнусную утечку памяти.

По вопросу 2 - локальный MyStruct уничтожается при выходе из функции;указатель MyStruct, который указывает на его возвращаемое значение, указывает на неопределенную область.Это может все еще работать, потому что ОС еще не восстановила память, но это определенно неправильное поведение - или безопасная вещь.

1 голос
/ 04 февраля 2010

Оба ваших вопроса касаются срока хранения и объема.

Во-первых, когда вы динамически выделяете объект, объект и указатель на него действительны, пока вы не освободите его. Если это автоматическая переменная (т. Е. Не динамически назначается new, malloc и т. Д. И не объявлена ​​static), переменная выходит из области видимости, как только заканчивается область действия объекта (обычно это } на том же «уровне», на котором был определен объект). Он также имеет «автоматическую продолжительность хранения», что означает, что хранилище для него также уходит, когда объект находится вне области действия.

Для вашего второго вопроса, tmp имеет область, которая заканчивается на }, равном create. Он также имеет одинаковую продолжительность хранения. Указатель на tmp действителен только в течение этого срока хранения. После выхода create() указатель на tmp становится недействительным и не может использоваться.

1 голос
/ 04 февраля 2010

Первый:

Mystruct* s = new Mystruct;

Часть new Mystryct выделяет память в куче для объекта этого типа.В C ++ он также выполняет конструктор по умолчанию типа.Часть Mystruct* s объявляет переменную-указатель, которая указывает на адрес первого байта вновь выделенной памяти объекта.

Second:

Mystruct s;

Это будет сделано так же, каксначала с двумя отличиями, которые можно упростить следующим образом: выделенная память для объекта находится в стеке, и нет переменной-указателя, указывающей на память, вместо этого s - это этот объект.Адрес этого объекта &s, поэтому указателю, указывающему на объект s, должно быть присвоено значение &s.

Почему MyStruct tmp не становится автоматически свободным 'd в конце create ()?

Да.Деструктор tmp запускает после оператора return, поэтому адрес, возвращаемый функцией, будет в памяти, которая вскоре будет перезаписана чем-то другим, что в лучшем случае приведет к ошибке сегментации (илиэквивалент) и в худшем случае испортит ваши данные.

0 голосов
/ 04 февраля 2010
struct MyStruct{ int i; };

MyStruct create(){ MyStruct tmp; tmp.i = 1337; return tmp; }

int main(){

  MyStruct s = create();
  cout << s.i;

  return 0;
}

или

struct MyStruct{ int i; };

MyStruct* create(){ MyStruct* tmp = new MyStruct; tmp->i = 1337; return tmp; }

int main(){

  MyStruct* s = create();
  cout << s->i;
  delete s;    

  return 0;
}

будет работать.Потому что конструктор копирования создаст копию структуры, когда она назначена s в первом случае.Весь новый / удаляемый материал (динамическое распределение памяти) относится к основам C ++.Вам не нужно использовать какие-либо новые или удалять для реализации основных алгоритмов.Конструктор копирования и т. Д. Всегда выполнит эту работу и облегчит понимание кода.

Если вы хотите использовать новый, вы должны также прочитать об автоопределении и так далее.В C ++ нет сборки мусора памяти.Я думаю, Мышление C ++ очень хорошо объясняет понятия динамической памяти (глава 13).

0 голосов
/ 04 февраля 2010
  1. В Mystruct *s = new Mystruct есть статическая переменная указатель, которая выделяется в начале вызова функции в стеке.Когда эта строка работает, она выделяет в куче структуру.В Mystruct s он размещает структуру в стеке, «статически», когда функция запускается (конструктор, если таковой имеется, будет выполняться в строке деклерации).t MyStruct tmp автоматически освобождается в конце создания ()? "- ну, это так.Но память все еще существует, поэтому вы можете получить к ней доступ, и она может содержать старые значения.

0 голосов
/ 04 февраля 2010

Mystruct *s = new Mystryct выделяет на куче.
Нужно явно удалить его.
Принимая во внимание, что Mystruct s выделяет структуру в стеке.
Он автоматически освобождается при разматывании стека (переменная выходит из области видимости) поэтому для случая 2 tmp будет освобожден, как только завершится создание create (). То, что вы вернули, это dangling pointer which is very dangerous.

Если это слишком сложно, следуйте этому правилу,
For every new operator called, delete must be called in the end.<br> For every new[] operator called, delete[] must be called in the end.

использовать умные указатели, которые автоматически удаляют выделенную память вместо обычных указателей. Если вы возвращаете объекты из функции, как в create, убедитесь, что вы выделяете ее с помощью оператора new, а не в стеке, как вы делали в примере. Убедитесь, что вызывающий вызывает delete для указателя, как только он закончит с ним.

0 голосов
/ 04 февраля 2010

1:

Mystruct *s = new Mystryct;

Создает структурную переменную в куче, на которую указывает переменная s.

Mystruct s;

Здесь структура создается в стеке.

Q2:

MyStruct *create(){ MyStruct tmp; tmp.i = 1337; return &tmp; }

не так !! Вы создаете локальную переменную структуры в стеке, которая исчезает, когда функция возвращается и любая ссылка на нее будет недействительной Вы должны динамически распределить переменную, и вам придется вручную освобождать ее, скажем, в main.

0 голосов
/ 04 февраля 2010

Mystruct * s = новая Mystruct;

Динамически размещает s в куче.Он не будет автоматически освобожден.(Также s является указателем, а не непосредственно Mystruct).

Mystruct s;

Статически выделяет s в стекеОн будет «освобожден», когда выйдет из области видимости. *

Ваш код недействителен.Когда вы ссылаетесь на tmp вне create, вы используете дикий указатель для доступа к мертвой памяти.Это приводит к неопределенному поведению.

  • В значительной степени.Использование его вне области действия - неопределенное поведение, даже если значение все еще находится в памяти.
...