Is 'int main; действительная программа C/С++?

Я спрашиваю, потому что мой компилятор, похоже, так думает, хотя я этого не делаю.

echo 'int main;' | cc -x c - -Wall
echo 'int main;' | c++ -x c++ - -Wall

Clang не вызывает никаких предупреждений или ошибок, и gcc выдает только незначительное предупреждение: 'main' is usually a function [-Wmain], но только тогда, когда скомпилировано как C. Указание -std=, похоже, не имеет значения.

В противном случае он компилируется и связывается в порядке. Но при выполнении он немедленно заканчивается на SIGBUS (для меня).

Чтение (отличных) ответов на Что должно было бы возвратиться main() в C и С++? и быстрый grep через спецификации языка, это, безусловно, Мне кажется,, что требуется основная функция. Но словосочетание gccs -Wmain ('main < обычно функция) (и недостаток ошибок здесь), по-видимому, может предложить иначе.

Но почему? Есть ли какой-то странный краевой вариант или "историческое" использование для этого? Кто-нибудь знает, что дает?

Моя точка зрения, я полагаю, заключается в том, что я действительно думаю, что это должна быть ошибка в размещенной среде, а??

110
задан Geoff Nixon 05 янв. '15 в 12:58
источник поделиться
9 ответов

Поскольку вопрос двузначен как C и С++, рассуждения для С++ и C будут разными:

  • С++ использует манипуляцию имени, чтобы помочь компоновщику различать текстовые идентичные символы разных типов, например. глобальная переменная xyz и независимая глобальная функция xyz(int). Однако имя main никогда не искажается.
  • C не использует mangling, поэтому программа может запутать компоновщик, предоставив символ одного вида вместо другого символа и успешно связав программу.

Вот что происходит здесь: компоновщик ожидает найти символ main, и он это делает. Он "проставляет" этот символ, как если бы он был функцией, потому что он не знает ничего лучшего. Часть библиотеки времени выполнения, которая передает управление main, запрашивает линкер для main, поэтому компоновщик дает ему символ main, позволяя завершить этап ссылки. Конечно, это не выполняется во время выполнения, потому что main не является функцией.

Вот еще одна иллюстрация той же проблемы:

file x.c:

#include <stdio.h>
int foo(); // <<== main() expects this
int main(){
    printf("%p\n", (void*)&foo);
    return 0;
}

file y.c:

int foo; // <<== external definition supplies a symbol of a wrong kind

компилирования:

gcc x.c y.c

Это компилируется и, вероятно, будет работать, но это поведение undefined, потому что тип символа, обещанный компилятору, отличается от фактического символа, предоставленного компоновщику.

Что касается предупреждения, я думаю, что это разумно: C позволяет создавать библиотеки, у которых нет функции main, поэтому компилятор освобождает имя main для других целей, если вам нужно определить переменную main по неизвестной причине.

97
ответ дан dasblinkenlight 05 янв. '15 в 13:13
источник поделиться

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

Конечно:

  • предупреждение полезно, поскольку это довольно подвержено ошибкам;
  • у вас может быть исходный файл без функции main() (библиотеки).

ИЗМЕНИТЬ

Некоторые ссылки:

  • main не является зарезервированным словом (С++ 11):

    Функция main не должна использоваться в программе. Связь (3.5) of main определяется реализацией. Программа, которая определяет основные как удалено или объявляет main inline, static или constexpr плохо сформирован. Имя main не является иным зарезервированный. [Пример: функции-члены, классы и перечисления могут быть называемый main, а также объекты в других пространствах имен. - конец примера]

    С++ 11 - [basic.start.main] 3.6.1.3

    [2.11/3] [...] некоторые идентификаторы зарезервированы для использования реализацией С++ и стандартными библиотеками (17.6.4.3.2) и не должны использоваться иначе; диагностика не требуется.

    [17.6.4.3.2/1] Определенные наборы имен и сигнатур функций всегда зарезервированы для реализации:

    • Каждое имя, содержащее двойное подчеркивание __ или начинающееся с символа подчеркивания, за которым следует заглавная буква (2.12), зарезервировано для реализации для любого использования.
    • Каждое имя, начинающееся с символа подчеркивания, зарезервировано для реализации для использования в качестве имени в глобальном пространстве имен.
  • Зарезервированные слова в языках программирования.

    Зарезервированные слова не могут быть переопределены программистом, но предопределенные функции часто могут быть переопределены в некоторой емкости. Это относится к main: есть области, в которых объявление, использующее этот идентификатор, переопределяет его значение.

30
ответ дан manlio 05 янв. '15 в 13:11
источник поделиться

Является ли int main; допустимой программой C/С++?

Не совсем понятно, что такое программа C/С++.

Является ли int main; действительной программой C?

Да. Внештатной реализации разрешено принимать такую ​​программу. main не обязательно должен иметь какое-либо особое значение в автономной среде.

Недействительно в размещенной среде.

Является ли int main; допустимой программой на С++?

То же.

Почему он падает?

Программа не должна иметь смысла в вашей среде. В автономной среде запуск и завершение программы и значение main определяются реализацией.

Почему компилятор предупреждает меня?

Компилятор может предупредить вас о том, что ему нравится, если он не отклоняет соответствующие программы. С другой стороны, предупреждение - это все, что требуется для диагностики несоответствующей программы. Поскольку эта единица перевода не может быть частью действительной размещенной программы, диагностическое сообщение оправдано.

Является ли gcc автономной средой или является размещенной средой?

Да.

gcc документирует флаг компиляции -ffreestanding. Добавьте его, и предупреждение исчезнет. Вы можете использовать его при создании, например. ядер или прошивки.

g++ не документирует такой флаг. Поставка, похоже, не влияет на эту программу. Вероятно, безопасно предположить, что среда, предоставляемая g++, размещена. Отсутствие диагностики в этом случае является ошибкой.

19
ответ дан n.m. 05 янв. '15 в 22:55
источник поделиться

Это предупреждение, так как это технически не разрешено. Код запуска будет использовать расположение символа "main" и перейти к нему с тремя стандартными аргументами (argc, argv и envp). Это не так, и в течение времени ссылки нельзя проверить, что это фактически функция, и даже не имеет этих аргументов. Вот почему работает int main (int argc, char ** argv) - компилятор не знает об аргументе envp, и это просто не используется, и это очистка вызывающего абонента.

Как шутка, вы можете сделать что-то вроде

int main = 0xCBCBCBCB;

на машине x86 и, игнорируя предупреждения и подобные вещи, он не будет просто компилироваться, но на самом деле работать тоже.

Кто-то использовал технику, подобную этой, чтобы написать исполняемый файл (вид), который выполняется на нескольких архитектурах напрямую - http://phrack.org/issues/57/17.html#article. Он также использовался для победы в IOCCC - http://www.ioccc.org/1984/mullender/mullender.c.

17
ответ дан dascandy 05 янв. '15 в 15:58
источник поделиться

Является ли это действующей программой?

Нет.

Это не программа, поскольку у нее нет исполняемых частей.

Правильно ли это компилировать?

Да.

Можно ли его использовать с действующей программой?

Да.

Не все скомпилированные коды должны быть действительными. Примерами являются статические и динамические библиотеки.

Фактически вы создали объектный файл. Это недействительный исполняемый файл, однако другая программа может ссылаться на объект main в результирующем файле, загружая его во время выполнения.

Если это ошибка?

Традиционно С++ позволяет пользователю делать то, что может показаться, что они не имеют действительного использования, но которые соответствуют синтаксису языка.

Я имею в виду, что это можно реклассифицировать как ошибку, но почему? Какая цель будет служить тому предупреждению?

Пока существует теоретическая возможность использования этой функциональности в реальном коде, маловероятно, что наличие нефункционального объекта с именем main приведет к ошибке в соответствии с языком.

9
ответ дан Michael Gazonda 05 янв. '15 в 21:27
источник поделиться

Я хотел бы добавить к ответам, уже приведенным, ссылаясь на фактические языковые стандарты.

Is 'int main; действительная программа C?

Короткий ответ (мое мнение): только если ваша реализация использует "автономную среду исполнения".

Все следующие цитаты из C11

5. Окружающая среда

Реализация преобразует исходные файлы C и выполняет C-программы в две системы обработки данных, которые будут называться среда перевода и среда выполнения [...]

5.1.2 среды выполнения

Определены две среды исполнения: автономные и размещенные. В оба случая, запуск программы происходит, когда назначенная функция C вызванный средой выполнения.

5.1.2.1 Внештатная среда

В автономной среде (в которой выполнение программы C может место без каких-либо преимуществ операционной системы), имя и тип функции, вызванной при запуске программы, определяются реализацией.

5.1.2.2 Хостинг среды

Хозяйственная среда не должна предоставляться, но должна соответствовать следующие характеристики, если они есть.

5.1.2.2.1 Запуск программы

Функция, вызванная при запуске программы, называется main. [...] Он должен быть определен с типом возврата int и без параметров [...] или с двумя параметрами [...] или эквивалентными или в некоторых других определенным образом.

Из этого следует следующее:

  • Программа C11 может иметь автономную или размещенную среду выполнения и быть действительной.
  • Если у него есть отдельно стоящий, нет основной функции.
  • В противном случае должен быть один с возвратной шкалой типа int.

В автономной среде исполнения я бы утвердил, что это действительная программа, которая не позволяет запускаться, потому что нет функции для этого, как требуется в 5.1.2. В размещенной среде выполнения, в то время как ваш код вводит объект с именем main, он не может предоставить возвращаемое значение, поэтому я бы сказал, что он не является допустимой программой в этом смысле, хотя можно было бы так же спорить, как если бы программа не была предназначенный для выполнения (например, может потребоваться предоставить данные только для примера), то это просто не позволяет делать именно это.

Is 'int main; действительная программа на С++?

Короткий ответ (мое мнение): только если ваша реализация использует "автономную среду исполнения".

Цитата из С++ 14

3.6.1 Основная функция

Программа должна содержать глобальную функцию main, которая является назначенный старт программы. Определяется реализация, независимо от того, программа в автономной среде требуется для определения основного функция. [...] Он должен иметь тип возврата типа int, но в противном случае его тип определяется реализацией. [...] Название не является иначе зарезервировано.

Здесь, в отличие от стандарта C11, меньшие ограничения применяются к автономной среде исполнения, так как никакой функции запуска не упоминается вообще, в то время как для размещенной среды исполнения это почти то же самое, что и для C11.

Опять же, я бы сказал, что для размещенного случая ваш код не является допустимой программой на С++ 14, но я уверен, что это для свободного случая.

Так как в моем ответе рассматривается среда исполнения, я думаю, что ответ dasblinkenlicht вступает в игру, так как имя, искажающееся в среде перевода, происходит заранее. Здесь я не уверен, что приведенные выше цитаты соблюдаются настолько строго.

6
ответ дан Ingo Schalk-Schupp 07 янв. '15 в 0:36
источник поделиться

Моя точка зрения, я полагаю, заключается в том, что я действительно думаю, что это должна быть ошибка в размещенной среде, а??

Ошибка за вами. Вы не указали функцию с именем main, которая возвращает int и попыталась использовать вашу программу в размещенной среде.

Предположим, что у вас есть единица компиляции, которая определяет глобальную переменную с именем main. Это вполне может быть законным в автономной среде, поскольку то, что составляет программу, остается за реализацией в автономных средах.

Предположим, что у вас есть другая единица компиляции, которая определяет глобальную функцию с именем main, которая возвращает int и не принимает аргументов. Это именно то, что требуется программе в размещенной среде.

Все отлично, если вы используете только первый блок компиляции в автономной среде и используете только вторую в размещенной среде. Что делать, если вы используете оба в одной программе? В С++ вы нарушили одно правило определения. Это поведение undefined. В C вы нарушили правило, которое диктует, что все ссылки на один символ должны быть согласованными; если это не поведение undefined. undefined поведение - это "выйти из тюрьмы, бесплатно!" карты разработчикам реализации. Все, что реализовано в ответ на поведение undefined, соответствует стандарту. Реализация не должна предупреждать, не говоря уже об обнаружении поведения undefined.

Что делать, если вы используете только один из этих блоков компиляции, но используете неправильный (что вы сделали)? В C ситуация четкая. Невозможность определить функцию main в одной из двух стандартных форм в размещенной среде - это поведение undefined. Предположим, вы вообще не определили main. Компилятор/компоновщик не должен ничего говорить об этой ошибке. То, что они жалуются, - это тонкость от их имени. То, что программа C, скомпилированная и связанная без ошибок, является вашей ошибкой, а не компилятором.

Это немного менее понятно в С++, поскольку неспособность определить функцию main в размещенной среде - это ошибка, а не поведение undefined (другими словами, она должна быть диагностирована). Однако одно правило определения в С++ означает, что линкеры могут быть довольно глупыми. Задача компоновщика решает внешние ссылки, и благодаря одному правилу определения компоновщик не должен знать, что означают эти символы. Вы предоставили символ с именем main, компоновщик ожидает увидеть символ с именем main, так что все будет хорошо, если речь идет о компоновщике.

4
ответ дан David Hammen 06 янв. '15 в 18:08
источник поделиться

Для C пока это определенное поведение реализации.

Как указано в ISO/IEC9899:

5.1.2.2.1 Запуск программы

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

int main(void) { /* ... */ }

или с двумя параметрами (называемыми здесь argc и argv, хотя любые имена могут быть используются, поскольку они являются локальными для функции, в которой они объявлены):

int main(int argc, char *argv[]) { /* ... */ }

или эквивалент; или каким-либо другим способом реализации.

4
ответ дан dhein 07 янв. '15 в 10:06
источник поделиться

Нет, это не действительная программа.

Для С++ это было недавно явно плохо сформировано отчет о дефекте 1886: привязка языка для main(), в котором говорится:

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

и часть разрешения включала следующее изменение:

Программа, объявляющая главную переменную в глобальной области действия или объявляющую название main с привязкой языка C (в любом пространстве имен), плохо сформирована.

Мы можем найти эту формулировку в последнем С++ проекте стандарта N4527, который является проектом С++ 1z.

Последние версии как clang, так и gcc теперь делают это ошибкой (видеть его в прямом эфире):

error: main cannot be declared as global variable
int main;
^

До этого отчета о дефекте было поведение undefined, которое не требует диагностики. С другой стороны, плохо сформированный код требует диагностики, компилятор может сделать это предупреждение или ошибку.

3
ответ дан Shafik Yaghmour 06 окт. '15 в 15:53
источник поделиться

Другие вопросы по меткам