*
и &
являются одними из первых препятствий, которые должны преодолеть программисты, плохо знакомые с C и C ++.
Чтобы по-настоящему понять эти концепции, полезно узнать немного больше о том, как память работает на этих языках.
Прежде всего: C ++ - это просто C, но с классами и многими другими дополнительными функциями. Почти все программы C являются действительными программами на C ++. C ++ даже начинался как язык, который сначала компилировался в C.
Память, грубо говоря, разделена на две части: «стек» и «куча». Есть и другие места для самого кода и констант времени компиляции (и, может быть, еще несколько) и так далее, но это пока не имеет значения. Переменные, объявленные внутри функции, всегда находятся в стеке. Давайте рассмотрим это в действии на простом примере и проанализируем, как организована память для построения ментальной модели.
#include <iostream>
void MyFunction() {
int intOnStack = 5;
int* intPtrOnStack = new int(6); // This int pointer points to an int on the heap
std::cout << intOnStack << *intPtrOnStack;
delete intPtrOnStack;
}
int main() { MyFunction(); }
Эта программа печатает 56
при выполнении. Так что же происходит, когда вызывается MyFunction()
? Во-первых, часть стека зарезервирована для этой функции для работы. Когда переменная intOnStack
объявлена внутри функции, она помещается в эту часть стека и инициализируется (заполняется) значением int
5
.
Далее переменная intPtrOnStack
объявлено. intPtrOnStack
относится к типу int*
. int*
указывает на int
, указав их адрес памяти. Таким образом, int*
помещается в стек и инициализируется значением, которое следует из выражения new int(6)
. Это выражение создает новый int в куче и возвращает ему адрес памяти этого int
(int*
). Это значит, что intPtrOnStack
теперь указывает на int
в куче. Хотя сам указатель живет в стеке.
Куча - это часть памяти, которая «разделяется» всеми функциями и объектами в программе. Стек не. Каждая функция имеет свою собственную часть стека, и когда функция заканчивается, ее часть стека освобождается.
Так что int*
- это просто адреса памяти int
. Неважно, где живет int
. int*
также может указывать на int
в стеке:
#include <iostream>
void MyFunction() {
int intOnStack = 5;
int* intPtrOnStack = &intOnStack; // This int pointer points to intOnStack
std::cout << intOnStack << *intPtrOnStack;
}
int main() { MyFunction(); }
Это печатает 55
. В этом примере мы также видим, что &
-оператор в действии (есть несколько вариантов использования &
, например, побитовый, и я не буду вдаваться в них).
&
просто возвращает адрес памяти (указатель!) своего операнда. В этом случае его операндом является intOnStack
, поэтому он возвращает свой адрес памяти и присваивает ему intPtrOnStack
.
До сих пор мы видели только int*
в качестве типов указателей, но существуют указатели. типы для каждого типа объекта, который имеет адрес памяти, включая указатели. Это означает, что такая вещь, как int**
существует и просто означает «указатель на указатель на int
». Как бы вы получили один? Например: &intPtrOnStack
.
Могут ли указатели жить только в стеке? № new int*(&intPtrOnStack)
Или new int*(new int(5))
.