Перш ніж переглядати питання щодо того, що відбувається, важливо зазначити, що програма неправильно сформована відповідно до звіту про дефекти 1886: Мовна зв'язок для main () :
[...] Програма, яка оголошує змінну main в глобальному масштабі або яка оголошує ім'я main за допомогою посилання на мову C (у будь-якому просторі імен), неправильно формується. [...]
Останні версії clang і gcc роблять це помилкою, і програма не буде компілюватись ( див. Приклад gcc live ):
error: cannot declare '::main' to be a global variable
int main = ( std::cout << "C++ is excellent!\n", 195 );
^
То чому ж не було діагностики у старих версіях gcc та clang? Цей звіт про дефекти навіть не мав запропонованої постанови до кінця 2014 року, тому цей випадок був зовсім недавно явно неправомірним, що вимагає діагностики.
До цього, здається , що це було б невизначений поведінка , так як ми порушений Shall вимоги проекту C ++ стандарт з розділу 3.6.1
[basic.start.main] :
Програма повинна містити глобальну функцію, яку називають основною, яка позначається початком програми. [...]
Невизначена поведінка непередбачувана і не вимагає діагностики. Невідповідність, яку ми спостерігаємо при відтворенні поведінки, є типовою невизначеною поведінкою.
Отже, що насправді робить код і чому в деяких випадках він дає результати? Давайте подивимось, що у нас є:
declarator
| initializer----------------------------------
| | |
v v v
int main = ( std::cout << "C++ is excellent!\n", 195 );
^ ^ ^
| | |
| | comma operator
| primary expression
global variable of type int
У нас є, main
що є int, оголошеним у глобальному просторі імен та ініціалізується, змінна має статичну тривалість зберігання. Це визначається, чи буде ініціалізація відбуватися до того, як буде зроблена спроба виклику main
, але, здається, gcc робить це перед тим, як викликати main
.
У коді використовується оператор комами , лівий операнд - це вираження відкинутого значення і тут використовується виключно для побічного ефекту виклику std::cout
. Результатом оператора кома є правильний операнд, який у цьому випадку є первинним значенням, 195
яке присвоюється змінній main
.
Ми можемо бачити, як sergej вказує на створені покази збірки, які cout
викликаються під час статичної ініціалізації. Хоча найцікавішим моментом для обговорення дивіться сеанс прямого бомба :
main:
.zero 4
та наступні:
movl $195, main(%rip)
Ймовірний сценарій полягає в тому, що програма переходить до символу, main
очікуючи, що дійсний код буде там, а в деяких випадках стане seg-fault . Отже, якщо це так, ми очікуємо, що зберігання дійсного машинного коду у змінній main
може призвести до працездатної програми , припускаючи, що ми знаходимось у сегменті, що дозволяє виконувати код. Ми можемо побачити, що ця запис МОКЦ 1984 року робить саме це .
Здається, ми можемо зробити gcc зробити це в C за допомогою ( дивіться це наживо ):
const int main = 195 ;
Це seg-помилки, якщо змінна main
не є const, мабуть, тому що вона не знаходиться у виконуваному місці, Hat Порада до цього коментаря тут, який дав мені цю ідею.
Також дивіться відповідь FUZxxl тут на C-конкретну версію цього питання.