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

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

const int main = 195;

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

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

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

const int main = 195;
          ^

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


person Destructor    schedule 23.10.2015    source источник
comment
По стандарту это просто неопределенное поведение.   -  person melpomene    schedule 23.10.2015
comment
@ machine_1 195 - это кодировка для кода операции ret (возврат из функции) на 8086 и его преемниках. Вы можете догадаться, что происходит, когда вы помещаете это в переменную и вызываете эту переменную как функцию.   -  person fuz    schedule 23.10.2015
comment
Вероятно, уместно указать ссылку на Как может работать программа с глобальной переменной с именем main вместо функции main?   -  person Shafik Yaghmour    schedule 23.10.2015
comment
Вы специально выбрали значение, чтобы оно совпадало с ret инструкцией?   -  person Ruslan    schedule 24.10.2015
comment
@Ruslan Если вы немного поищете, вы можете найти различные версии этого в нескольких местах. В сети обмена стеками это была одна из старых ссылок. В моем ответе на ссылку выше мы можем найти запись IOCCC 1984 года, которая делает что-то похожее, но гораздо более изощренно.   -  person Shafik Yaghmour    schedule 25.10.2015


Ответы (2)


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

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

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

  • Сегмент text является (если поддерживается архитектурой) защищенным от записи и исполняемым и содержит исполняемый код, переменные static продолжительности хранения, определенные 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 и без параметров (...) или с двумя параметрами (...) или эквивалентными; или каким-либо другим способом, определяемым реализацией.

person fuz    schedule 23.10.2015
comment
Некоторые хорошие моменты, zwol затронул некоторые из них здесь в комментариях к моему ответу на аналогичную версию этого вопроса на C ++. - person Shafik Yaghmour; 23.10.2015
comment
Пожалуйста, включите свой комментарий о кодировке кода операции ret в свой ответ. Это ключ к пониманию описанного поведения. - person Dekay; 23.10.2015
comment
@ user19474 Так лучше? - person fuz; 23.10.2015
comment
@FUZxxl: хороший ответ. Но ваш ответ не объясняет, почему программа выходит со значением мусора в качестве статуса возврата вместо 0? Было бы лучше, если бы вы могли объяснить причину этого. - person Destructor; 15.12.2015
comment
@PravasiMeet Инструкция ret завершает текущую функцию. Он не устанавливает возвращаемого значения. Таким образом, программа завершается с тем, что было в регистре eax на момент возврата main, то есть со случайным мусорным значением. - person fuz; 15.12.2015

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

Использование main в качестве переменной в глобальной области видимости делает поведение программы неопределенным.

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

person Bathsheba    schedule 23.10.2015
comment
Неа. Платформа может позволить объявить main «способом, определяемым реализацией». - person fuz; 23.10.2015
comment
Я слишком стар, чтобы разбираться в стандарте, но я полагаю, что это должно быть всегда функцией! - person Bathsheba; 23.10.2015
comment
Ср. ISO 9899: 2011 §5.1.2.2.1 «Функция, вызываемая при запуске программы, называется main. Реализация не объявляет прототип для этой функции. Он должен быть определен с типом возвращаемого значения int и без параметров: (...) или с двумя параметрами (называемыми здесь argc и argv, хотя могут использоваться любые имена, поскольку они являются локальными для функции, в которой они объявлены): (...) или эквивалент; 10) или каким-либо другим способом, определяемым реализацией ». - person fuz; 23.10.2015
comment
В ПОРЯДКЕ. Хорошая стандартная ссылка! Я внес поправки. - person Bathsheba; 23.10.2015