Где хранятся переменные в C ++? - PullRequest
12 голосов
/ 23 октября 2008

Где хранятся переменные в C ++?

Внутри оперативной памяти или кеша процессора?

Ответы [ 9 ]

35 голосов
/ 23 октября 2008

Переменные хранятся:

  • в стеке, если они auto -матические локальные переменные функции
  • в куче, если они выделены с помощью new или malloc и т. Д. (Подробности того, что значит сказать "переменная хранится в куче" в комментариях)
  • в области данных для процесса, если они глобальные или static

Это все в оперативной памяти, конечно. Кэширование прозрачно для процессов пользовательского пространства, хотя оно может заметно повлиять на производительность.

Компиляторы могут оптимизировать код для хранения переменных в регистрах. Это сильно зависит от компилятора и кода, но хорошие компиляторы будут делать это агрессивно.

16 голосов
/ 23 октября 2008

Для C ++ в общем случае правильный ответ - «куда бы ваш компилятор не решил их поместить». В противном случае вы не должны делать предположений, если только вы как-то не руководите своим компилятором. Некоторые переменные могут храниться целиком в регистрах, а некоторые могут быть полностью оптимизированы и заменены где-то литералом. В некоторых компиляторах на некоторых платформах константы могут фактически оказаться в ПЗУ.

Часть вашего вопроса о "кэше процессора" немного запутана. Существуют некоторые инструменты для управления обработкой кэша процессором, но в целом это бизнес процессора и он должен быть для вас невидимым. Вы можете думать о кеше как о окне вашего процессора в оперативной памяти. В значительной степени любой доступ к памяти проходит через кеш.

На другом конце уравнения неиспользуемая ОЗУ иногда выгружается на диск в большинстве операционных систем. Так что возможно (но маловероятно), что в некоторые моменты ваши переменные фактически сохраняются на диске. : -)

8 голосов
/ 23 октября 2008

Переменные обычно хранятся в оперативной памяти. Это либо в куче (например, все глобальные переменные, как правило, идут туда), либо в стеке (все переменные, объявленные в методе / функции, обычно идут туда). Stack и Heap - это оперативная память, просто разные места. У указателей разные правила. Сам указатель на что-то (блок памяти, объект и т. Д.) Обычно следует приведенным выше правилам (указатель, объявленный внутри функции, хранится в стеке), но данные, на которые он указывает (сам блок памяти или объект вы создали с новым) хранится в куче. Вы можете создавать указатели, указывающие на стек (например, «int a = 10; int * b = & a;», b указывает на a, а a хранится в стеке), но выделение памяти с использованием malloc или новых счетчиков к куче памяти.

То, что входит в кэш ЦП, находится вне контроля компиляторов, ЦП сам решает, что кешировать и как долго его кэшировать (в зависимости от таких факторов, как «Эти данные использовались недавно?» Или «Следует ли ожидать, что данные снова используются довольно скоро? »и, конечно, размер кеша оказывает большое влияние на то, как долго процессоры будут хранить там данные - чем больше у них кеша, тем больше данных они могут кешировать и тем дольше они могут хранить данные там раньше. освобождение кеш-памяти для новых данных).

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

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

C ++ не знает о кеше вашего процессора.

Когда вы запускаете программу, написанную на C ++ или любом другом языке, ваш процессор будет хранить копию «популярных» кусков оперативной памяти в кэше. Это сделано на аппаратном уровне.

Не думайте, что кэш-память процессора - это «другая» или «дополнительная» память ... это всего лишь механизм, позволяющий держать некоторые куски оперативной памяти рядом.

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

Переменные в C ++ хранятся либо в стеке, либо в куче.

стек:

int x;

ворох:

int *p = new int;

При этом обе структуры встроены в ОЗУ.

Если объем используемой оперативной памяти высок, хотя Windows может заменить его на диск.

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

4 голосов
/ 23 октября 2008

Я думаю, что вы смешиваете две концепции. Во-первых, как язык C ++ хранит переменные в памяти. Во-вторых, как компьютер и операционная система управляют этой памятью.

В C ++ переменные могут быть размещены в стеке, который является памятью, которая зарезервирована для использования программой и имеет фиксированный размер при запуске потока или в динамической памяти, которая может быть выделена на лету с помощью new. Компилятор также может хранить переменные в регистрах процессора, если это позволяет анализ кода. Эти переменные никогда не увидят системную память.

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

1 голос
/ 17 января 2013

Язык C ++ поддерживает два вида выделения памяти через переменные в программах на C ++:

Статическое распределение - это то, что происходит, когда вы объявляете статическую или глобальную переменную. Каждая статическая или глобальная переменная определяет один блок пространства фиксированного размера. Пространство выделяется один раз, когда ваша программа запускается (часть операции exec), и никогда не освобождается. Автоматическое распределение происходит, когда вы объявляете автоматическую переменную, такую ​​как аргумент функции или локальная переменная. Пространство для автоматической переменной выделяется при вводе составного оператора, содержащего объявление, и освобождается при выходе из этого составного оператора. Размер автоматического хранилища может быть выражением, которое варьируется. В других реализациях CPP это должна быть константа. Третий важный тип выделения памяти, динамическое распределение, не поддерживается переменными C ++, но доступен для функций библиотеки. Динамическое выделение памяти

Динамическое распределение памяти - это метод, при котором программы во время работы определяют, где хранить некоторую информацию. Динамическое распределение требуется, когда объем памяти, который вам нужен, или длительность, в которой вы продолжаете в ней нуждаться, зависят от факторов, которые неизвестны до запуска программы.

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

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

Когда вы используете динамическое распределение, выделение блока памяти - это действие, которое программа явно запрашивает. Вы вызываете функцию или макрос, когда хотите выделить место, и указываете размер с помощью аргумента. Если вы хотите освободить место, вы делаете это, вызывая другую функцию или макрос. Вы можете делать это когда угодно, так часто, как захотите.

Динамическое распределение не поддерживается переменными CPP; отсутствует класс хранения «динамический», и никогда не может быть переменной CPP, значение которой хранится в динамически выделенном пространстве. Единственный способ получить динамически распределенную память - это системный вызов, а единственный способ обратиться к динамически распределенному пространству - через указатель. Поскольку это менее удобно, а фактический процесс динамического выделения требует большего времени вычислений, программисты обычно используют динамическое распределение только тогда, когда ни статическое, ни автоматическое распределение не будут служить.

Например, если вы хотите динамически выделить некоторое пространство для хранения struct foobar, вы не можете объявить переменную типа struct foobar, содержимое которой является динамически выделяемым пространством. Но вы можете объявить переменную типа указателя struct foobar * и присвоить ей адрес пробела. Затем вы можете использовать операторы ‘*’ и ‘->’ в этой переменной указателя для ссылки на содержимое пробела:

 {
   struct foobar *ptr
      = (struct foobar *) malloc (sizeof (struct foobar));
   ptr->name = x;
   ptr->next = current_foobar;
   current_foobar = ptr;
 }
1 голос
/ 23 октября 2008

Переменные могут храниться в разных местах, иногда в нескольких местах. Большинство переменных помещаются в оперативную память при загрузке программы; иногда переменные, которые объявлены const, вместо этого помещаются в ПЗУ. Всякий раз, когда к переменной обращаются, если она не находится в кеше процессора, произойдет пропадание кеша, и процессор остановится, пока переменная будет скопирована из ОЗУ / ПЗУ в кеш.

Если у вас есть какой-то наполовину приличный оптимизирующий компилятор, локальные переменные часто вместо этого будут храниться в регистровом файле процессора. Переменные будут перемещаться назад и вперед между ОЗУ, кешем и регистровым файлом по мере их чтения и записи, но обычно они всегда будут иметь копию в ОЗУ / ПЗУ, если только компилятор не решит, что это не нужно.

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

в зависимости от того, как они объявлены, они будут либо сохранены в « heap », либо в « stack »

Куча - это динамическая структура данных, которую может использовать приложение.

Когда приложение использует данные, оно должно быть перемещено в регистры ЦП непосредственно перед их использованием, однако это очень изменчивое и временное хранилище.

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