2009-09-03 3 views
32

Когда я пишу программу и скажу ей int c=5, она помещает значение 5 в немного своей памяти, но как она помнит, какой из них? Единственный способ, о котором я мог думать, - это иметь еще немного памяти, чтобы рассказать об этом, но тогда он должен был бы помнить, где он это сохранил, и как он помнит, где все?Как узнать, где моя ценность в памяти?

+21

Я думаю, что он печатает его на листе бумаги. На всякий случай. –

+5

+1 для проклятого прекрасного первого вопроса ... добро пожаловать в складку :) – skaffman

+4

@ skaffman Хорошо сказано. Трудно поставить себя на ноги абсолютного новичка, и я думаю, что это вопрос, который действительно показывает любопытство и мысли ... хотя я громко смеялся над комментарием хакера! –

ответ

9

Здесь есть много хороших ответов, но все они, кажется, пропустили один важный момент, который, по моему мнению, был основным направлением вопроса ОП, так что и здесь. Я говорю о скомпилированных языках, таких как C++, интерпретируемые являются гораздо более сложными.

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

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

+0

Хорошая работа, определяющая вопрос в вопросе. – dmckee

+0

Я подумал об этом, но не хотел усложнять проблему. Ну, вот оно. – Artelius

1

Вы должны изучить указатели.

http://home.netcom.com/~tjensen/ptr/ch1x.htm

+10

- это указатель на указатель? – skaffman

+1

Вопрос в том, что делает компилятор с указателями на объявленные переменные, хранит ли они их на переменных? :) Я думаю, что его черепахи полностью опущены. – voyager

13

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

Это, по крайней мере, общий принцип. На самом деле это будет более полно, но все-таки та же основная идея.

+2

Это похоже на компиляцию, которая перемаркирует каждую переменную, имеющую число, представляющее адрес в памяти. Итак, «c» становится «0xdeadbeef» или что-то еще. Повторная привязка в порядке, чтобы сделать по той же причине, что вы можете пройти весь исходный код и изменить ссылки «c» на ссылки «d», и программа по-прежнему делает то же самое (если бы не была уже переменная «d», defined!) –

6

чтения переменных (программирование) - выделение памяти:
http://en.wikipedia.org/wiki/Variable_(programming)#Memory_allocation

Вот текст из ссылки (если вы не хотите, чтобы на самом деле пойти туда, но вам не хватает все ссылки в тексте) :

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

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

Объекты, выделенные из кучи, должны быть восстановлены , особенно если объекты больше не нужны.В языке, собранном для мусора (например, C#, Java и Lisp) среда выполнения автоматически восстанавливает объекты , когда существующие переменные не могут больше ссылаться на них. В не связанных с мусором языках, таких как как C, программа (и программист) должна явно выделить память и , а затем освободить ее, чтобы вернуть ее память . Несоблюдение этого требования приводит к утечкам памяти , в которых куча истощается по мере запуска программы, рискуя Возможный отказ от изнурения доступная память.

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

+0

Как долго, по вашему мнению, кому-то понадобится следить за ссылкой, которую вы отправили, перефразируя то, что она говорит с красивым форматированием, и выиграть сам принятый ответ? –

+1

@Kyle: Звучит неплохо. Я проголосую за это. – Beska

+0

Они говорят это лучше, чем я мог. –

4

Он встроен в программу.

В принципе, когда программа скомпилирована в машинный язык, она становится серией инструкций. В некоторых инструкциях есть встроенные в них адреса памяти, и это, так сказать, «конец цепочки». Компилятор решает, где будет находиться каждая переменная, и записывает эту информацию в исполняемый файл. (Помните, что компилятор является РАЗНОЙ программой программы, которую вы пишете, просто сосредоточиться на том, как работает ваша собственная программа на данный момент.)

Например,

ADD [1A56], 15 

может добавить 15 к значения в месте 1A56. (Эта инструкция будет кодироваться с использованием кода, который процессор понимает, но я не буду это объяснять.)

Теперь в других инструкциях вы можете использовать «переменную» адрес памяти - адрес памяти, который был загружен из некоторых место нахождения. Это основа указателей в C. Вы, конечно, не можете иметь бесконечную цепочку из них, иначе у вас не хватит памяти.

Я надеюсь, что это прояснит ситуацию.

1

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

В интерпретируемом языке один регистр, если он часто зарезервирован для хранения указателя на структуру данных («среда»), которая связывает имена переменных с их текущими значениями.

6

Существует многоступенчатый танец, который превращает c = 5 в машинные инструкции для обновления местоположения в памяти.

  1. Компилятор генерирует код в двух частях. Есть часть инструкции (загрузите регистр с адресом C, загрузите регистр в литерал 5, магазин). И есть часть распределения данных (оставьте 4 байта комнаты со смещением 0 для переменной, известной как «C»).

  2. «Коммуникационный загрузчик» должен помещать этот материал в память так, чтобы ОС могла его запускать. Загрузчик запрашивает память, а ОС выделяет некоторые блоки виртуальной памяти. ОС также отображает виртуальную память в физическую память через несвязанный набор механизмов управления.

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

  4. Когда выполняется фактическая инструкция «store», ОС должна видеть, действительно ли ссылочная страница данных находится в физической памяти. Он может быть в файле подкачки и должен быть загружен в физическую память. Используемый виртуальный адрес преобразуется в физический адрес расположения памяти.

+0

Очень красивое, четкое резюме. –

3

Я собираюсь сформулировать свой ответ в очень простой терминологии. Пожалуйста, не оскорбляйтесь, я просто не уверен, насколько вы уже владеете и хотите дать ответ, приемлемый для того, кто может быть новичком.

Вы на самом деле не так далеко от своего предположения. Программа, с которой вы запускаете свой код, обычно называется компилятором (или интерпретатором, в зависимости от языка), отслеживает все используемые вами переменные. Вы можете думать о своих переменных как о серии ящиков, и отдельные части данных хранятся внутри этих ящиков. У бункеров есть надписи на них, и когда вы создаете исходный код в программе, которую вы можете запустить, все ярлыки переносятся вперед. Компилятор позаботится об этом для вас, поэтому, когда вы запускаете программу, правильные вещи извлекаются из соответствующего бункера.

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

1

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

Поскольку большинство из нас не являются учеными, машинный язык абстрагируется на язык ассемблера. Assemply - очень примитивный язык, который непосредственно контролирует память. Существует очень ограниченное количество команд (push/pop/add/goto), но в итоге они выполняют все, что запрограммировано. В разных машинных архитектурах есть разные версии сборки, но суть в том, что существует несколько десятков регистров памяти (физически в процессоре). В архитектуре x86 они EAX, EBX, ECX, EDX, ... Они содержат данные или которые CPU использует для определения того, что делать дальше. Процессор может делать только одно дело за раз, и он использует эти регистры, чтобы выяснить, что делать дальше. Кажется, что компьютеры могут делать много вещей одновременно, потому что процессор может обрабатывать эти инструкции очень быстро - (миллионы/миллиарды инструкций в секунду). Конечно, многоядерные процессоры усложняют ситуацию, но давайте не будем туда ...

Поскольку большинство из нас недостаточно умны или достаточно точны, чтобы программировать на сборке, где вы можете легко свернуть систему, сборка далее абстрагируется Язык третьего поколения (3GL) - это ваш C/C++/C#/Java и т. Д. ...Когда вы укажете одному из этих языков значение целочисленного значения 5 в переменной, ваши инструкции сохраняются в тексте; ассемблер компилирует ваш текст в файл сборки (исполняемый файл); когда программа выполняется, программа и ее команды ставятся в очередь CPU, когда это время показа для этой конкретной строки кода, оно считывается в регистре CPU и обрабатывается.

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

+0

Чтобы научить себя сбоку в каменном веке, я вручную закодировал двоичные коды (на 6502), а затем «вытолкнул» их из Basic-in-ROM. И в 80-е годы, где я работал, мне иногда приходилось загружать старые машины HP, переключая серию из 16 переключателей, чтобы вручную загружать регистры процессора, а затем нажмите кнопку «запустить». К счастью, это было только для режима обслуживания. – NVRAM

1

Существует немало недостатков, которые совершаются несколькими людьми, которые предполагают, что все переменные хранятся в памяти. Ну, если вы не считаете регистры процессора как память, тогда это будет не совсем правильно. Некоторые компиляторы оптимизируют сгенерированный код, и если они могут хранить переменную, хранящуюся в регистре, то некоторые компиляторы будут использовать это! Тогда, конечно, есть сложная проблема кучи и памяти стека. Локальные переменные могут быть расположены в обоих! Предпочтительное местоположение будет находиться в стеке, доступ к которому осуществляется чаще, чем куча. Это касается почти всех локальных переменных. Глобальные переменные часто являются частью сегмента данных конечного исполняемого файла и, как правило, становятся частью кучи, хотя вы не можете освободить эти глобальные области памяти. Но куча часто используется для «на лету» выделения новых блоков памяти, на alloc ating memory для них.

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

О, это даже немного сложнее, чем это, но уже я вижу, как некоторые глаза катятся из-за этой избыточной информации. :-)

1

Подумайте о памяти как ящике, в которую вы решите, как ее делить, в соответствии с вашими спонтанными потребностями.

Когда вы объявляете переменную типа integer или любого другого типа, компилятор или интерпретатор (в зависимости от того, который выбрал) выделяет адрес памяти в своем сегменте данных (регистр DS на ассемблере) и резервирует определенное количество следующих адресов в зависимости от типа вашего типа длина в бит.

В соответствии с вашим вопросом целое число составляет 32 бита, поэтому из одного заданного адреса, допустим, D003F8AC, 32 бита, следующие за этим адресом, будут зарезервированы для вашего объявленного целого.

В момент компиляции, если вы ссылаетесь на свою переменную, сгенерированный код ассемблера заменит его своим DS-адресом. Итак, когда вы получаете значение переменной C, процессор запрашивает адрес D003F8AC и извлекает его.

Надеюсь, это поможет, так как у вас уже есть много ответов. :-)

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