Поправьте меня, если я ошибаюсь, но указатели - это идентификаторы (ID), присвоенные каждому биту памяти. Если в памяти в куче есть указатели, ее можно идентифицировать, что дает коду возможность удалять, изменять или даже изменять ее размер. Путаница возникает, когда я читал книгу, в которой говорится, что в стеке нет указателей.
Я читал книгу Head First C, в которой говорилось об указателях. Было ли это просто ссылкой на указатели в качестве примера? Или в памяти в стеке есть какой-то указатель на память?
Если нет, то как это определить? Путаница или противоречие между публикациями в Интернете и книгами действительно сбили меня с толку по предмету, который я только сейчас понимаю. Кто-нибудь может это прояснить?
Указатель - это просто переменная, которая хранит адрес чего-то еще в качестве своего значения. Если вы обычно думаете о переменной, содержащей немедленные значения, например int a = 5;
, указатель будет просто содержать адрес, по которому хранится 5
, например int *b = &a;
. Другими словами, b
точки, где хранится значение a
(например, от b
точки до a
).
У каждого фрагмента памяти есть адрес. Указатель - это просто хранилище, содержащее значение которого является адресом памяти. У вас может быть указатель, содержащий любой адрес, который вы хотите, но ссылка на этот адрес может быть невозможна (некоторые из них каким-либо образом запрещено использовать).
Итак, как стек, так и куча имеют указатели на свои значения или указатели на их слоты памяти, или если char*
указывает на первый элемент этого массива? Или это всего лишь куча.
Указатель - это указатель, это указатель - он может содержать любой адрес - указывает ли он на адрес в стеке или указывает на выделенную память - не имеет значения. Это просто указатель. (это просто обычная переменная, которая содержит адрес чего-то еще) Указатель должен быть инициализирован и хранить действительный адрес для чего-то. Когда указатель объявлен, он равен неинициализированный, пока ему не будет присвоено значение. (плохие вещи случаются, когда люди пытаются использовать неинициализированные указатели ....)
У каждого объекта есть размер, и он складывает все, подождите, уложите один поверх другого. Вы продвигаетесь вверх и выскакиваете вниз. Никаких идентификаторов не требуется, вам просто нужно знать, сколько вещей надеть, а затем сколько снять, когда они выйдут за рамки. Компилятор позаботится об этом за вас.
Может помочь опубликовать точный текст из книги
И в качестве еще одного момента, чтобы облегчить ваше понимание, стек обычно используется для выделения места для переменных, объявленных в подпрограмме. Указатель на адрес памяти стека больше не будет действителен после возврата из подпрограммы, потому что этот адрес может и будет использоваться повторно.
По моему опыту, книги «Сначала голова» ужасны.
Возможно, вы не осознали, что существует указатель стека скрытый, создаваемый компилятором, а не вами, каждый раз, когда вы вызываете функцию. На все переменные стека ссылаются через смещения этого указателя, которые вы не видите.
Я предполагаю, что книга хочет сказать вам, что вы можете получить доступ к памяти в стеке без использования указателей. Вы можете просто использовать int var;
и более поздние версии var = 123;
в своей функции, и вам не нужно беспокоиться об указателях. Доступ к памяти в куче невозможен без явного использования указателей.
@MarkSetchell: Вы думаете о x86. Это не универсально. У SPARC был регистровый файл.
@MSalters, если ваша программа на C принимает адрес локального, компилятор SPARC должен поместить этот локальный адрес куда-нибудь
@Caleth: На самом деле, вы можете быть удивлены тем, насколько хорошими могут быть компиляторы. Например, если вы просто укажете адрес printf
, вы можете обнаружить, что компилятор никогда не потрудится пролить регистр. Правило «AS-IF» довольно мощное.
@MSalters Ок, "берет и использует нетривиальным образом адрес локального".
Каждая ячейка памяти имеет адрес. Нет ничего лучше, чем вы предполагаете, что стековая память не имеет адресов.
Значение указателя - это адрес ячейки памяти, а переменная-указатель - это переменная, которая может хранить значение указателя.
В C почти все имеет адрес. Исключение составляют временные значения и объекты, объявленные как register
.
На данные в стеке ссылаются по смещению от указателя стека / базы.
Когда ваша программа выполняется, часть памяти выделяется ОС для ее целей. Затем стек будет использоваться вашей программой для хранения данных, необходимых для нормального выполнения. Обратите внимание, что этот фрагмент памяти может быть не последовательным в физической памяти, а пространством виртуальной памяти, отображаемым ОС.
Когда переменная определяется локально в функции, компилятор сгенерирует код, который считывает фрагмент данных в стеке, который используется для представления вашей переменной. Компилятор позаботится о том, чтобы несколько переменных не занимали одно и то же пространство. Обратите внимание, что оптимизирующий компилятор может по-прежнему использовать одно и то же пространство для нескольких переменных, если предыдущие определения считаются бесполезными в потоке выполнения (не путать с сроком действия переменной в C++).
Однако этот процесс применим только в том случае, если вы точно знаете, сколько памяти вам нужно во время компиляции, что означает, что компилятор сможет сгенерировать смещение для каждой именованной переменной, определенной в вашей программе. Это означает, что нет необходимости явно читать ячейку памяти, иначе говоря, разыменование указателей. Однако это не означает, что сама переменная не находится в ячейке памяти и не имеет адреса, по которому на нее можно ссылаться.
При написании программ, требующих динамического распределения памяти, вам придется явно указать ОС выделить больше памяти для вашей программы. Поскольку эта ячейка памяти является динамической, адрес не будет известен во время компиляции, а это означает, что доступ к данным может осуществляться только через разыменование ячеек памяти. В этом случае это будет дополнительный кусок памяти, выделенный ОС.
И последнее, но не менее важное: компиляторы могут и всегда будут пытаться сгенерировать код, который хранит переменные в регистрах, поэтому обращение к переменной может сильно отличаться в зависимости от сгенерированного кода.
Нет гарантии, что стек непрерывен в пространстве виртуальной памяти. Это может быть связанный список, и в этом случае на данные не ссылается смещение. Помните, что функция не может напрямую обращаться к переменным стека от вызывающей стороны, поэтому нет необходимости располагать их со смещением относительно ее собственного.
Может быть, я неправильно * понимаю вас, не могли бы вы подробнее рассказать, как стек может быть связанным списком?
Похоже, вы неправильно поняли то, что говорилось в книге. Утверждение, которое вы приписываете книге, не имеет смысла.