Почему книги говорят: «Компилятор выделяет пространство для переменных в памяти». Разве это не исполняемый файл, который делает это? Я имею в виду, например, если я напишу следующую программу,
#include <iostream>
using namespace std;
int main()
{
int foo;
return 0;
}
и скомпилировать его, и получить исполняемый файл (пусть это будет program.exe), теперь, если я запустлю program.exe, этот исполняемый файл сам даст команду выделить место для переменной foo. Не так ли? Пожалуйста, объясните, почему книги продолжают говорить: «компилятор сделает это ... сделайте это».
sizeof
вопрос находится в разделе Почему sizeof называется оператором времени компиляции?Ответы:
Вы правы в том, что компилятор как таковой пропал, когда ваша программа действительно работает И если он работает на другом компьютере, компилятор больше не доступен.
Я предполагаю, что это должно сделать четкое различие между памятью, фактически выделенной вашим собственным кодом. Компилятор вставит некоторый код в вашу программу, который выполняет выделение памяти (например, используя команды new, malloc или аналогичные).
Поэтому книги часто используют «компилятор делает то или иное», чтобы сказать, что компилятор добавил некоторый код, который явно не упоминается в ваших файлах кода. Достаточно верно, что это не совсем то, что происходит. С этой точки зрения многие вещи, упомянутые в уроках, были бы неправильными, но потребовали бы довольно сложных объяснений.
источник
malloc
et. и др.Это зависит от переменной. ОС выделяет кучу, программа выделяет стек, а компилятор выделяет пространство для глобальных переменных / статики, то есть они встроены в сам исполняемый файл. Если вы выделите 1 МБ глобальной памяти, размер вашего exe увеличится как минимум на 1 МБ.
источник
int test[256][1024]; int main(){ test[0][0]=2; return 0; }
этой маленькой программы выделено 1 МБ, но генерируется только объектный файл объемом 1,4 КБ и исполняемый файл объемом 8,4 КБ. Тем не менее, он должен использовать правильный объем оперативной памяти.int a1=1,a2=2,
... вплоть до ..., a1048576=1048576;
Только тогда я определенно получу что-то большее, чем 1 Мб, я думаю.что компилятор будет сделать , это ваш код и скомпилировать его в машинный код. То, что вы упоминаете, является хорошим примером, когда компилятору нужно только перевести.
Например, когда вы пишете
Вы можете увидеть, что «я говорю компилятору [ в выводе, который он генерирует ] запрос, чтобы компьютер зарезервировал достаточно оперативной памяти для int, на который я могу ссылаться позже, компилятор, вероятно, будет использовать идентификатор ресурса или какой-то механизм для отслеживания foo в машинный код, вы можете использовать foo в текстовом файле вместо написания ассемблера! Ура !
Таким образом, вы также можете посмотреть на это, когда компилятор пишет письмо ( или, возможно, роман / энциклопедию ) всем целевым процессорам и устройствам. Письмо написано в двоичных сигналах, которые (как правило) могут быть переведены на разные процессоры путем изменения цели. Любое «письмо» и / или комбо может отправлять всевозможные запросы и / или данные - например, выделите место для этой переменной, которую использовал программист.
источник
Сказать «компилятор выделяет память» может быть не совсем точным в буквальном смысле, но это метафора, которая наводит на мысль о правильном пути.
Что действительно происходит, так это то, что компилятор создает программу, которая выделяет собственную память. За исключением того, что не программа распределяет память, а ОС.
Так что на самом деле происходит то, что компилятор создает программу, которая описывает требования к памяти, а ОС использует это описание и использует его для выделения памяти. За исключением того, что ОС является программой, и программы на самом деле ничего не делают, они описывают вычисления, которые выполняются процессором. За исключением того, что ЦП на самом деле является просто сложной электронной схемой, а не антропоморфизированным маленьким гомонкулом.
Но имеет смысл думать о программах, компиляторах и процессорах как о маленьких людях, которые живут внутри компьютера, не потому, что на самом деле они есть, а потому, что эта метафора хорошо подходит человеческому мозгу.
Некоторые метафоры хорошо работают для описания вещей на одном уровне абстракции, но не работают так же на другом уровне. Если вы думаете на уровне компилятора, имеет смысл описать процесс генерации кода, который приведет к выделению памяти, когда компилируемая программа фактически выполняется как «выделение памяти». Достаточно близко, что, когда мы думаем о том, как работает компилятор, у нас есть правильная идея, и это не так скучно, что мы забываем, что мы делали. Если мы попытаемся использовать эту метафору на уровне запущенной скомпилированной программы, она вводит в заблуждение странным образом, что вы и заметили.
источник
Компилятор решает, где хранить переменную - может быть в стеке или в свободном регистре. Каким бы ни было решение компилятора, принятое компилятором, соответствующий машинный код для доступа к этой переменной будет сгенерирован и не может быть изменен во время выполнения. В этом смысле компилятор отвечает за выделение пространства для переменных, а финальный файл program.exe просто слепо действует как зомби во время выполнения.
Теперь не путайте это с другим динамическим управлением памятью, таким как malloc, new или может быть вашим собственным управлением памятью. Компиляторы имеют дело с хранением и доступом к переменным, но не имеет значения, что означает фактическое значение в другой структуре / библиотеке. Например:
Во время выполнения malloc может возвращать произвольное число, но компилятору все равно, где его хранить.
источник
Более точная формулировка была бы: - «компилятор говорит загрузчику зарезервировать место для переменных»
В среде C-ish будет три типа пространства для переменных:
На современной ОС куча памяти фактически не будет зарезервирована, а будет выделена как требуется.
источник
Да, вы правы, в этом случае (объявляя переменную в функции) предложение вашей книги, вероятно, неверно: когда вы объявляете переменную в функции, она выделяется в стеке при входе в функцию. В любом случае, компилятор должен оптимизировать ситуацию: если функция нерекурсивна (
main()
является хорошим кандидатом на это), можно «распределить» ее время компиляции (на BSS).(Если вам интересно, где находятся ваши переменные, вы можете проверить это грязным способом (если вы не хотите проверять структуру файла obj, в любом случае, почему бы и нет?), Так что вы можете объявить некоторые другие виды переменных: константа, статические, динамические,
malloc()
-распределенные и т. д., и отображать их адреса (используйте%X
форматтерprintf()
для лучшей читаемости). Переменные, находящиеся в стеке, будут иметь очень разные адреса памяти.)источник
Единственное, что можно сделать во время выполнения, это увеличить указатель стека на определенную величину. Поэтому компилятор решает заранее:
Это можно назвать «распределением», но, конечно, во время компиляции оно занимает место только в той модели, которая есть у компилятора работающей программы.
источник