Почему const int main = 195 приводит к рабочей программе, но без const она заканчивается ошибкой сегментации?

Рассмотрим следующую программу C (см. живое демо здесь).

const int main = 195;

Я знаю, что в реальном мире ни один программист не пишет такой код, потому что он не служит никакой полезной цели и не имеет никакого смысла. Но когда я удаляю ключевое слово const сверху программы, это немедленно приводит к ошибке сегментации. Зачем? Я очень хочу знать причину этого.

GCC 4.8.2 дает следующее предупреждение при компиляции.

предупреждение: "main" обычно является функцией [-Wmain]

const int main = 195;
          ^

Почему присутствие и отсутствие ключевого слова const имеют здесь значение в поведении программы?

Ответ 1

Обратите внимание на то, как значение 195 соответствует инструкции ret (возврат из функции) на совместимости 8086. Таким образом, это определение main ведет себя так, как если бы вы определили его как int main() {} при выполнении.

На некоторых платформах данные const загружаются в исполняемую, но не записываемую область памяти, тогда как изменяемые данные (т.е. данные без квалификации const) загружаются в область записи, но не в исполняемую область памяти. По этой причине программа "работает", когда вы объявляете main как const, но не тогда, когда вы опускаете квалификатор const.

Традиционно двоичные файлы содержали три сегмента:

  • Сегмент text (если поддерживается архитектурой) защищен от записи и исполняемый файл и содержит исполняемый код, переменные статической продолжительности хранения const и строковые литералы
  • Сегмент data доступен для записи и не может быть выполнен. Он содержит переменные не квалифицированные const со статической продолжительностью хранения и (во время выполнения) объекты с выделенной продолжительностью хранения
  • Сегмент bss похож на сегмент data, но инициализируется ко всем нулям. Он содержит переменные статической продолжительности хранения, не квалифицированные const, которые были объявлены без инициализатора
  • Сегмент stack отсутствует в двоичном файле и содержит переменные с продолжительностью автоматического хранения

Удаление квалификатора const из переменной main заставляет его перемещаться из сегмента text в data, который не является исполняемым, что вызывает нарушение сегментации, которое вы наблюдаете.

Современные платформы часто имеют дополнительные сегменты (например, сегмент rodata для данных, которые не являются ни записываемыми, ни исполняемыми), поэтому, пожалуйста, не принимайте это как точное описание своей платформы, не консультируясь с конкретной документацией на платформе.

Пожалуйста, поймите, что не выполнение функции main, как правило, неверно, хотя технически платформа может позволить объявить main как переменную, ср. ISO 9899: 2011 §5.1.2.2.1 ¶1, акцент мой:

1 Функция, вызванная при запуске программы, называется main. Реализация не объявляет прототипа для этой функции. Он должен быть определен с типом возврата int и без параметров (...) или с двумя параметрами (...) или эквивалентным; или каким-либо другим способом реализации.

Ответ 2

В C, main в глобальной области видимости почти всегда есть функция.

Чтобы использовать main как переменную в глобальной области видимости, выполните поведение программы undefined.

(Возможно, может быть, когда вы пишете const, компилятор оптимизирует переменную до константы, и поэтому поведение вашей программы отличается, но поведение программы по-прежнему undefined).