Является ли адрес локальной переменной constexpr?
В книге Бьярна Страуструпа "Язык программирования C++ (4-е издание)" на с. 267 (Раздел 10.4.5 Выражения констант адреса), он использует пример кода, где адрес локальной переменной устанавливается constexpr
переменной constexpr
. Я думал, что это выглядит странно, поэтому я попытался запустить пример с g++ версии 7.3.0 и не смог получить те же результаты. Вот его пример кода (дословно сокращенно):
extern char glob;
void f(char loc) {
constexpr const char* p0 = &glob; // OK: &glob is a constant
constexpr const char* p2 = &loc; // OK: &loc is constant in its scope
}
Когда я запускаю это, я получаю:
error: ‘(const char*)(& loc) is not a constant expression
Что-то происходит с g++, о котором я не знаю, или есть что-то еще в примере Бьярне?
Ответы
Ответ 1
Более ранняя печать книги Бьярна Страуструпа "Язык программирования C++ (4-е издание)" на с. 267 имеет ошибку, изложенную в вопросе OP. Текущие печатные и электронные копии были "исправлены", но в них была внесена другая ошибка, описанная ниже. Теперь это относится к следующему коду:
constexpr const char* p1="asdf";
Это нормально, потому что "asdf" хранится в фиксированной ячейке памяти. В более ранней печати книга ошибается здесь:
void f(char loc) {
constexpr const char* p0 = &glob; // OK: &glob is a constant
constexpr const char* p2 = &loc; // OK: &loc is constant in its scope
}
Однако loc
не находится в фиксированной ячейке памяти. это в стеке и будет иметь различные местоположения в зависимости от того, когда он вызывается.
Однако в печати четвертого издания есть еще одна ошибка. Это дословный код из 10.5.4:
int main() {
constexpr const char* p1 = "asdf";
constexpr const char* p2 = p1; // OK
constexpr const char* p3 = p1+2; // error: the compiler does not know the value of p1
}
Это не верно. Компилятор/компоновщик знает значение p1 и может определить значение p1+2
во время соединения. Компилируется просто отлично.
Ответ 2
Похоже, что пример из раздела 10.4.5, представленный в моей печатной копии "Язык программирования C++ (4-е издание)", неверен. И поэтому я пришел к выводу, что адрес локальной переменной не является constexpr
.
Пример, по-видимому, был обновлен в некоторых версиях PDF, как показано здесь:
![enter image description here]()
Ответ 3
Этот ответ пытается прояснить, почему адрес локальной переменной не может быть constexpr
, анализируя пример для архитектуры x86-64.
Рассмотрим следующую игрушечную функцию print_addr()
, которая отображает адрес своей локальной переменной local_var
и рекурсивно вызывает себя n
раз:
void print_addr(int n) {
int local_var{};
std::cout << n << " " << &local_var << '\n';
if (!n)
return; // base case
print_addr(n-1); // recursive case
}
Вызов print_addr(2)
привел к следующему выводу в моей системе x86-64:
2 0x7ffd89e2cd8c
1 0x7ffd89e2cd5c
0 0x7ffd89e2cd2c
Как видите, соответствующие адреса local_var
различны для каждого вызова print_addr()
. Вы также можете видеть, что чем глубже вызов функции, тем ниже адрес локальной переменной local_var
. Это связано с тем, что на платформе x86-64 размер стека уменьшается (т.е. С более высоких адресов на более низкие).
Для вывода выше, стек вызовов будет выглядеть следующим образом на платформе x86-64:
| . . . |
Highest address ----------------- <-- call to print_addr(2)
| print_addr(2) |
-----------------
| print_addr(1) |
-----------------
| print_addr(0) | <-- base case, end of recursion
Lowest address ----------------- Top of the stack
Каждый прямоугольник выше представляет кадр стека для каждого вызова print_addr()
. local_var
каждого вызова находится в соответствующем кадре стека. Поскольку local_var
каждого вызова print_addr()
находится в своем собственном (другом) стековом фрейме, адреса local_var
различаются.
В заключение, поскольку адрес локальной переменной в функции может не совпадать для каждого вызова функции (т.е. Каждый кадр стека вызовов может быть расположен в другой позиции в памяти), адрес такой переменной может не может быть определено во время компиляции, и поэтому не может быть квалифицировано как constexpr
.
Ответ 4
Просто чтобы добавить к другим ответам, которые указали на ошибку, стандарт C++ разрешает указатели constexpr только на объекты со статической продолжительностью хранения, один за другим или nullptr
. Смотрите [expr.const/8] конкретно # 8.2;
Стоит отметить, что:
- строковые литералы имеют статическую длительность хранения:
- Основываясь на ограничениях в объявлении
extern
переменных, они по своей природе будут иметь длительность статической памяти или длительность локальной памяти потока.
Следовательно, это действительно:
#include <string>
extern char glob;
std::string boom = "Haha";
void f(char loc) {
constexpr const char* p1 = &glob;
constexpr std::string* p2 = nullptr;
constexpr std::string* p3 = &boom;
}
Ответ 5
dsaaaffffffasddddqwccasdqwdascasc