В таком случае данные будут сохраняться в стеке?

С недавним открытием, что код <code class="cpp">const int* Number = &amp;static_cast&lt;const int&amp;&gt;(1);</code> является допустимым, возникает вопрос: было ли это разрешено всегда? Ранее я полагал, что указатель может ссылаться только на lvalue или на данные в куче.
  • 10 октября 2024 г. 6:06
Ответы на вопрос 5
В приведенном вами коде есть несколько моментов, которые стоит обсудить.

Для начала, рассмотрим ваше утверждение о том, что "указатель может ссылаться только на lvalue или на данные в куче". Это не совсем так. Указатели в C++ могут указывать на любые объекты, включая временные (temporary) значения, которые являются rvalue.

Ваша строка кода:

```cpp
const int* Number = &static_cast<const int&>(1);
```

является допустимой, но есть некоторые нюансы:

1. В C++ числовая литерал `1` является rvalue. Однако, когда вы используете `static_cast<const int&>(1)`, вы создаете временный объект, который может быть "привязан" к lvalue-ссылке, потому что временные значения могут быть связаны с константной lvalue-ссылкой.

2. Временные объекты в C++ существуют в области видимости, пока они не перестанут использоваться, но в вашем случае — вы имеете дело с указателем на временный объект, который будет уничтожен сразу после завершения выражения. Это означает, что указатель `Number` будет указывать на память, которая больше не действительна, и доступ к этой памяти приведет к неопределенному поведению.

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

Если же вы хотите сохранить единицу как `const int`, вам стоит использовать статическую переменную или динамическое выделение памяти:

```cpp
const int temp = 1;
const int* Number = &temp;
```

Или:

```cpp
const int* Number = new int(1);  // Не забывайте освобождать память позже
```

Теперь `Number` будет указывать на валидный объект, чья жизнь будет длиться дольше, чем жизнь временного объекта.
В наше время можно просто проверить: godbolt.org 

И clang и g++ и при локальном и при глобальном объявлении кладут 1 на стек.
Правда, clang чуть поумнее и выдает warning:
warning: temporary whose address is used as value of local variable 'Number' will be destroyed at the end of the full-expression [-Wdangling]


Нельзя надеяться, что по этому адресу останется лежать 1. В более сложных случаях этот адрес может быть переиспользован под что-то еще. Обращение по такому указателю - UB.
Не пиши так, пожалуйста. 
Перед нами продление жизни временного объекта.
Когда ссылка исчезнет — а она исчезнет после точки с запятой — указатель будет смотреть в никуда, и только от Ктулху зависит, когда переменную перезапишут.
Правильно так:
const int& ref = 1;
const int* Number = &ref;

Данные могут лежать где угодно — теоретически на стеке, но оптимизатор может перекинуть их и в сегмент данных.

В общем, правило. Продлённый объект живёт, пока живёт та ссылка, что его продлила. Ссылки и указатели, что сделаны уже из этой ссылки, не в счёт: Си++ всё-таки не «мусорный» язык. Временный объект живёт до точки с запятой , за исключением нескольких случаев: явная команда придержать объект (о которой у нас и речь), создание/копирование массива (для простоты компиляции и чтобы не раздувать стек), с Си++23 в команде «цикл по объекту» ради безопасности и предсказуемости.
Данные будут храниться в сегменте .rodata или .text
Если это определение внутри функции/метода - то Number будет лежать на стеке. 
Если вне функции/метода - в области глобальных данных.
А вот данные на которые указывает Number могут лежать где угодно.
Да, указатель может указывать и на данные на стеке или в глобальной области. В общем на любой выделенный блок памяти он может указывать. А в вашем примере даже и не на выделенный, только обращаться по такому указателю нельзя.
Похожие вопросы