2013-05-07 4 views
2

Я читал о том, как выделяется память на C++.Организация памяти в C++

Несколько ресурсов упомянуть:

http://www.geeksforgeeks.org/memory-layout-of-c-program/

http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory

Object creation on the stack/heap?

Global memory management in C++ in stack or heap?

http://msdn.microsoft.com/en-us/library/vstudio/dd293645.aspx

Heap/Stack and multiple processes

Do different programs gets their memory from a common heap or from a separate heap?

http://computer.howstuffworks.com/c28.htm

enter image description here

Я хочу прояснить несколько моментов, на основе моего чтения:

В соответствии с http://www.geeksforgeeks.org/memory-layout-of-c-program/ Раздел 4 Стек «Стек, где автоматическая сохраняются переменные, а также информация, которая сохраняется каждый раз е функция называется»

Пусть:

class myClass{ 
    int a; 
    char b; 
    public: 
    myClass(int a,char b) 
    { 
    this->a = a; 
    this->b = b; 
    } 
}; 

1) Согласно тому, что я прочитал, когда мы составляем этот код двоичный сидит в памяти программ и ничего не было выделено на стек еще. Верный?

Сейчас в моей главной:

int main() 
{ 
myClass Ob(1,'c'); 
return 0; 
} 

2) Теперь объект Ob размерных 5 байт (4 байта (INT), 1 байт (символов) - 32-разрядная ОС) создается на стеке, так как оно является автоматической переменной. Верный ?

3) Когда вызывается конструктор myClass(int a,char b), временные переменные (параметры a, b) создаются в стеке для конструктора, а затем уничтожаются после создания объекта Ob? Например, когда мы вызываем функцию, передавая параметры по значению.

Теперь предположим, что другой класс

class pointerClass { 
int a; 
char* b; 
public: 
pointerClass(int size){ 
b= new char[size]; 
a=size; 
} 
}; 

Сейчас в основной:

int main() 
{ 
pointerClass ptr(10) ; //Step 1 
} 

4) Означает ли это PTR объект размером 8 байт (INT A (4 байта), символ * B (4 байты, т. е. это просто удерживающий адрес, указывающий на кучу) создается в стеке? Далее память объемом 10 байт (соответствующая новому символу [10] выделена на куче), на которую указывает содержимое char * b? Am I исправить?

5) Когда мы передаем параметр функции по ссылке, например fn (int *a,char* b) или fn(int& a,char& b), означает ли это, что временный указатель/ссылка создается в стеке для функции, которая указывает на фактический объект, который передается и уничтожается при возврате функции?или, скорее, фактический объект передается вместо создания и уничтожения временного указателя/ссылки на стек для этой функции?

Это я спросил вчера, но я не удовлетворен ответом: Constructor, Copy Constructor and Stack Creation : C++

6) Когда мы перегружать Fn, таких как fn(int a,char b)fn(int& a,char& b) мы можем назвать из основной, как fn(A,B) с ниже литой static_cast<void(*)(int, char)>(fn)(a, c); //Calls fn(int a,char b) static_cast<void(*)(int&, char&)>(fn)(a, c);//Calls fn(int& a.char& b) Что именно здесь происходит? Что пусто (*).

Благодаря

+7

Думаю, вам нужно разделить этот вопрос на несколько мелких вопросов. Вы ожидаете, что мы напишем книгу C++ в качестве ответа? –

+0

Я думаю, что все вопросы связаны, поэтому ставится под один вопрос. И ответ «да» или «нет» в основном .. Так что нечего набирать при ответе .. –

+0

@Mat Отредактировал мой вопрос –

ответ

4
  1. правильно
  2. правильно (хотя это может быть не пять байтов, вероятно, восемь)
  3. правильно
  4. правильно
  5. временный указатель/ссылка создается на стеке, а не конечно, почему вы не были довольны ответом, данным ранее, это выглядит правильно для меня
  6. void(*)(int,char) является типом, sp ecifically указатель на функцию void с двумя аргументами и int и char. По-видимому, этот бросок заставляет компилятор выбрать версию функции, которую вы хотите, хотя для меня это новость, что это возможно.

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

+0

Что касается первого вопроса: я бы сказал, что это во многом зависит от того, как вы определяете «стек» и некоторые другие термины. «1) Согласно тому, что я прочитал, когда мы скомпилируем этот код, двоичный файл находится в памяти программ, и ничто еще не было выделено в стеке». компиляция! = загрузка программы (или выполнения) и т. д. – dyp

+0

john .. Относительно 5: Ответ, который я получил, не был временной ссылкой или указатель создан на стеке, а объект передается напрямую ... Можете ли вы передать мне какую-либо ссылку на прочитайте, если у вас есть что-нибудь для этого? –

+2

@GauravK Я думаю, вы неправильно поняли ответ «В любом случае копия объекта ** не создается **». (мой акцент). – john

6
  1. Правильно. Выделение происходит во время выполнения.
  2. Частично правильный - стандарт не использует термины «стопка» и «куча», он просто требует поведения от объектов. Тем не менее, стек является наиболее распространенным и обычным способом для достижения этой функциональности. Кроме того, компиляторам разрешено заполнять структуры байтами заполнения, поэтому размер объекта не следует предполагать. Это называется structure padding. Просто используйте sizeof, чтобы получить размер.
  3. Частично Правильно. Для передачи и возврата по значению нужен конструктор копирования для доступа к вызовам, но эти вызовы могут быть устранены при обстоятельствах. Этот процесс известен как copy elision.
  4. Частично правильный - указатель просто указывает на объект с динамическим хранилищем. Размер указателя может меняться.
  5. Указатель или ссылка создается локально для функции, но она указывает или ссылается на объект, адрес которого передан. Здесь нет копии, и ничего не происходит.
  6. Каждая переменная имеет тип данных в C и C++. Typecasting позволяет вам гибко заставить компилятор обрабатывать указатель на один из данных типа, который должен рассматриваться как указатель на совершенно другой тип данных . Поскольку функции имеют тип, указатели на функции тоже имеют тип, а приведение типов позволяет заставить компилятор обрабатывать указатель на функцию от одного типа функций до совершенно другого типа, тем самым, по существу, позволяя вам вызывать нужную версию перегруженной функции.
+0

«Размер указателя может отличаться». может варьироваться в зависимости от реализации на C++ (т. е. компилятора, целевой платформы и т. д.), но постоянной в рамках скомпилированной программы. – dyp

+0

@juanchopanza: Отсюда * Частично правильный * в пункте 3. Уточнил его, чтобы устранить запутанную формулировку. –

+0

Ну, мне нравится этот ответ, так что +1, но у меня есть несколько замечаний: «Распределение происходит во время выполнения», есть время загрузки и инициализация структур, необходимых для библиотеки (например, некоторые строчные функции с строкой C). Я спросил о «динамической памяти», потому что я знаю только из «динамической продолжительности хранения» Std (-> new, delete), но указатели также могут указывать на объекты статической или автоматической (или потоковой локальной) продолжительности хранения. 6) преобразование требуется для разрешения перегрузки. – dyp

3

Во-первых, я должен указать, что диаграмма, которую вы показываете, очень зависит от системы. Например, в Solaris операционная память не отображается вообще в адресное пространство пользователя. И самое частое отображение имеет только три отображаемых сегмента для пользовательской памяти, по крайней мере, в начале программы: код сегмент внизу (но не абсолютный снизу, поскольку адрес , как правило, не будет отображаться) , сегмент данных над ним, и сегмент стека (который растет вниз) вверху, с большим отверстием нераспределенной памяти между стеком и данными.

Все это полностью изменяется, как только вы начинаете динамически .

  1. No. После Комплексы код (в более широком смысле компиляции), исполняемый файл находится в файле, а не в памяти. Программа не загружается в память до ее выполнения. (Там раньше некоторые исключения в начале Unix, и во встраиваемых систем, даже сегодня. Но для систем общего назначения как Windows, и современные Unix, это верно.)

  2. переменная будет создана на стек. Но почти , конечно, будет больше 5 байтов из-за выравнивания соображений. (Восемь байтов будут минимальными для большинства 32 битовых машин.) В C++ создание объекта - это двухэтапный процесс: выделение памяти и вызов конструктора. В большинстве реализаций вся память, которая понадобится для , все объекты, используемые в функции, будут выделены немедленно, в верхней части функции; в некоторых случаях дополнительная память также будет выделена с каждой стороны каждой переменной, для объяснения причин отладки, и память будет инициализирована. Конструктор объекта будет вызываться, когда поток программы передает определение объекта.

  3. Да. Вызов конструктора в точности как вызов функции . Опять же, создание аргументов - это двухэтапный процесс; в этом случае стратегии различаются: некоторые реализации будут выделить всю память, необходимую для любых аргументов в верхней части функции, другие будут выделять их по мере необходимости , перед их инициализацией. И в случае простых переменных , таких как int, большинство машин имеют одну инструкцию , которая позволит назначить и инициализации в той же инструкции . В зависимости от типов аргументов и того, как они инициализируются , компилятор может даже использовать разные стратегии .

  4. Правильно, более или менее. Для встроенных типов, таких как int или указатели, единственное «уничтожение» освобождает память, а в зависимости от компилятора может не произойти до конца вызывающей функции. (С другой стороны, если вы вызываете вторую функцию, эта память будет повторно использована. Таким образом, программа ведет себя точно так, как будто «память была немедленно освобождена.)

  5. Правильно, более или менее. (Формально, ссылки не являются «уничтожены», потому что они не являются объектами. Практически, по крайней мере, , когда они используются в качестве параметров функции, основной код точно так же, как и для указателей.)

  6. Во-первых, только вещь, которую вы можете сделать с результатами a static_cast на указателе на функцию, - это вернуть ее обратно в его первоначальный тип. Все остальное - неопределенное поведение. Если fn определяется как void fn(int a, char b), используя результаты static_cast<void (*) (int&, char&)>(fn) не определен поведение, и не будет работать. То, что здесь происходит, может быть практически ни о чем, но есть хороший шанс, что это приведет к сбою программы . И void (*) в этом случае - часть декларации указателя на тип функции; void (*)(int, char ), является названием типа: указатель (*) для работы ( (int, char) — круглые скобки необходимы из-за правил приоритета), возвращающихся void.

EDIT:

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

+0

+1 для этого всестороннего и точного ответа. – dyp

+0

@ DyP Опечатка, да. –

Смежные вопросы