C / C ++: всегда ли встроенные объекты, такие как `int« double`, всегда остаются в точном месте в памяти во время работы программы?

Делать встроенные типы, которые не определяется динамически, всегда оставаться в одном и том же фрагменте памяти в течение всей программы?

Если это что-то, что я должен понять, как мне пройтись и проверить это?

то есть

int j = 0;
double k = 2.2;
double* p = &k;

Движется ли архитектура системы или компилятор вокруг всех этих объектов, если программа на C / C ++, скажем, сильно потребляет память?

Примечание: я не говорю о контейнерах, таких как std::vectors<T>, Очевидно, что они могут перераспределять в определенных ситуациях, но, опять же, это динамично.


побочный вопрос:
Следующий сценарий, очевидно, поднимет несколько бровей. Просто в качестве примера, будет ли этот указатель всегда действительным во время работы программы?

Этот побочный вопрос устарел, благодаря моему невежеству!

struct null_deleter
{
void operator() (void const *) const {};
};

int main()
{
// define object
double b=0;

// define shared pointer
std::shared_ptr<double> ptr_store;
ptr_store.reset(&b,null_deleter()); // this works and behaves how you would expect
}

2

Решение

В абстрактной машине адрес объекта не изменяется в течение времени жизни этого объекта.

(Слово «объект» здесь не относится к «объектно-ориентированному» объекту; «объект» — это просто область хранения.)

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

Например, это:

int n;
int *addr1 = &n;
int *addr2 = &n;
if (addr1 == addr2) {
std::cout << "Equal\n";
}

должен вывести «Equal» — но умный оптимизирующий компилятор может юридически исключить все, кроме оператора вывода.

Стандарт ISO C прямо указывает это в разделе 6.2.4:

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

с (ненормативной) сноской:

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

Я не нашел аналогичного явного утверждения в стандарте C ++; или я скучаю по этому, или авторы сочли это слишком очевидным, чтобы утверждать.

6

Другие решения

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

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

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

double k = 2.2;
double *p = &k;
*p = 3.3;

вероятно эквивалентно этому:

double k = 3.3;
1

И да и нет.

Глобальные переменные останутся на том же месте.

Переменные стека (внутри функции) будут выделяться и освобождаться каждый раз, когда функция вызывается и возвращается. Например:

void k(int);
void f() {
int x;
k(x);
}
void g() {
f();
}
int main() {
f();
g();
}

Здесь во второй раз f() называется, это x будет в другом месте.

1

Есть несколько ответов на этот вопрос, в зависимости от факторов, которые вы не упомянули.

  • Если адрес объекта данных никогда не берется, то соответствующая C-программа не может определить, является ли он даже имеет адрес. Он может существовать только в регистрах или может быть полностью оптимизирован; если он существует в памяти, ему не нужно иметь фиксированный адрес.
  • Объекты данных с «автоматом» срок хранения (в первом приближении, локальные переменные функции не объявлены с static) создаются каждый раз, когда их содержащая функция вызывается и уничтожается при выходе; их может быть несколько копий в любой момент времени, и нет никакой гарантии, что у нового экземпляра тот же адрес, что и у старого.
  • Мы говорим о & оператор как «получение адреса» объекта данных, но с технической точки зрения это не то, что он делает. Это конструирует указатель к этому объекту данных. Указатели являются непрозрачными объектами в стандарте C. Если вы проверяете биты (путем преобразования в целое число), результат определяется реализацией. И если вы проверяете биты дважды подряд, нет гарантии, что вы получите одинаковое число! Гипотетическая сборка мусора на C мог отслеживайте все указатели на каждый элемент данных и обновляйте их по мере необходимости, когда он перемещал кучу. (Люди действительно пробовали это. Это имеет тенденцию ломать программы, которые не придерживаются буквы правил.)
0