ИМХО, Питер предоставил объяснение в своем комментарии:
Если указатель инициализирован, чтобы содержать адрес переменной, и этот указатель может быть доступен из другого модуля компиляции, то это будетразумно, чтобы компилятор допускал возможность разыменования указателя после инициализации в некотором модуле компиляции, который не виден компилятору.Одним из следствий этого является не оптимизация указателя или переменной вне существования.Существует множество других подходов к рассуждению, которые могут привести к тому же результату, в зависимости от того, какой код компилятор может видеть на самом деле.
, и это именно то, что я тоже думаю.
The const
в C ++ немного сбивает с толку.Это выглядит как сокращение от «константа», но на самом деле это означает «только для чтения».
Имея это в виду, я никогда не задумывался, почему следующий код допустим в C:
enum { N = 3 };
static int a[N]; /* legal in C: N is a constant. */
но это не так:
const int n = 3;
static int b[n]; /* illegal in C: n is a read-only variable */
Когда я переключился на C ++, я принимал вышеизложенное для C ++, пока не понял в беседе с коллегой, что я был неправ.(Не то чтобы это нарушало любой мой письменный код, но я ненавижу, что это неправильно.); -)
const int n = 3;
static int b[n]; // legal in C++
и Const-распространение - это метод, который делает его законным.
Однако даже при распространении const const int x;
все еще остается переменной только для чтения, к которой можно обратиться.
ОП предоставил ссылку на эту тему (которая могла бы объяснить это даже лучше, чем выше):
SO: почему размер массива как константы не разрешен в C, но разрешен в C ++?
Чтобы сделать этот полнофункциональный ответ, я попыталсяподготовить образец, который иллюстрирует различия:
#include <iostream>
const int x1 = 1;
static const int x1s = 11;
extern const int x1e = 12;
const int x2 = 2;
extern const int *const pX2 = &x2;
const int x3 = 3;
static const int *const pX3 = &x3;
int main()
{
// make usage of values (to have a side-effect)
std::cout << x1;
std::cout << x1s;
std::cout << x1e;
std::cout << x2;
std::cout << pX2;
std::cout << x3;
std::cout << pX3;
// done
return 0;
}
и результат gcc 8.2
с -O3
:
; int main()
main:
; {
sub rsp, 8
; // make usage of values (to have a side-effect)
; std::cout << x1;
mov esi, 1
mov edi, OFFSET FLAT:_ZSt4cout
call _ZNSolsEi
; std::cout << x1s;
mov esi, 11
mov edi, OFFSET FLAT:_ZSt4cout
call _ZNSolsEi
; std::cout << x1e;
mov esi, 12
mov edi, OFFSET FLAT:_ZSt4cout
call _ZNSolsEi
; std::cout << x2;
mov esi, 2
mov edi, OFFSET FLAT:_ZSt4cout
call _ZNSolsEi
; std::cout << pX2;
mov esi, OFFSET FLAT:_ZL2x2
mov edi, OFFSET FLAT:_ZSt4cout
call _ZNSo9_M_insertIPKvEERSoT_
; std::cout << x3;
mov esi, 3
mov edi, OFFSET FLAT:_ZSt4cout
call _ZNSolsEi
; std::cout << pX3;
mov esi, OFFSET FLAT:_ZL2x3
mov edi, OFFSET FLAT:_ZSt4cout
call _ZNSo9_M_insertIPKvEERSoT_
; // done
; return 0;
; }
xor eax, eax
add rsp, 8
ret
и ИМХО самая интересная часть - глобальные переменные:
; const int x3 = 3;
_ZL2x3:
.long 3
; extern const int *const pX2 = &x2;
pX2:
.quad _ZL2x2
; const int x2 = 2;
_ZL2x2:
.long 2
; extern const int x1e = 12;
x1e:
.long 12
x1
, x1s
и pX3
были оптимизированы, поскольку они const
и не помечены для внешней связи.
x1e
и pX2
были выделены, поскольку они помечены для внешней связи.
x2
выделено, поскольку оно обозначено pX2
, который отмечен для внешней связи.(Кое-что из extern может получить доступ к x2
через pX2
.)
x3
было самым сложным для меня.pX3
было использовано (в std::cout << pX3;
).Хотя само его значение указывается в значении x3
.Кроме того, хотя доступ к x3
(в std::cout << x3;
) также был встроен, использование указателя pX3
, инициализированного с &x3
, не позволило оптимизировать это хранилище.
Демонстрация в реальном времени на Godbolt (с приятным цветным двойным обзором, облегчающим исследование)
Я сделал то же самоес clang 7.0.0
и результат был аналогичным.
(я пробовал также с msvc v19.15
, но я не смог (недостаточно терпеливо) оценить результат.)
Что касается 4., я попытался дополнительно:
const int x4 = 4;
static const int *const pX4 = &x4;
и добавил к main()
:
std::cout << x4;
// No: std::cout << pX4;
В этом случае не было выделенного хранилища - ни для x4
, ни дляpX4
.(pX4
был оптимизирован, не оставляя «ссылки» на x4
, который, в свою очередь, также был оптимизирован.)
Это удивительно ...