С недавним открытием, что код <code class="cpp">const int* Number = &static_cast<const int&>(1);</code> является допустимым, возникает вопрос: было ли это разрешено всегда? Ранее я полагал, что указатель может ссылаться только на lvalue или на данные в куче.
В приведенном вами коде есть несколько моментов, которые стоит обсудить. Для начала, рассмотрим ваше утверждение о том, что "указатель может ссылаться только на 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 могут лежать где угодно.
Да, указатель может указывать и на данные на стеке или в глобальной области. В общем на любой выделенный блок памяти он может указывать. А в вашем примере даже и не на выделенный, только обращаться по такому указателю нельзя.