Є 'int main;' дійсна програма C / C ++?


113

Я прошу, тому що, здається, мій компілятор так вважає, хоча я цього і не роблю.

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

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

В іншому випадку він добре компілює і посилається. Але по виконанню він припиняється одразу SIGBUS(для мене).

Прочитавши (відмінні) відповіді у розділі Що має головне () повернутися в C та C ++? і швидкий пробір через мовні характеристики, мені, звичайно, здається , що потрібна основна функція . Але багатослівність від gcc's -Wmain('main', як правило, є функцією) (і дефіцит помилок тут), здається, можливо підказує інше.

Але чому? Чи є для цього якесь дивне краєвидне чи «історичне» використання? Хтось знає, що дає?

Думаю, я вважаю, що я дійсно думаю, що це повинно бути помилкою в розміщеному середовищі, так?


6
Щоб зробити gcc (переважно) стандартним компілятором, вам потрібноgcc -std=c99 -pedantic ...
pmg

3
@pmg Це те саме попередження, з або без -pedanticабо будь-яке -std. Моя система c99також збирає це без попередження або помилок ...
Джефф Ніксон

3
На жаль, якщо ви "досить розумні", ви можете створювати речі, прийнятні компілятором, але не мають сенсу. У цьому випадку ви зв’язуєте бібліотеку виконання C для виклику змінної main, яка називається , що навряд чи спрацює. Якщо ви ініціалізуєте main з "правильним" значенням, воно може насправді повернутися ...
Mats Petersson

7
І навіть якщо це дійсно, це жахливо робити (нечитабельний код). BTW, це може бути різним у розміщених реалізаціях та у вільних реалізаціях (про які не знаю main)
Basile Starynkevitch

1
Для більш веселих часів спробуйтеmain=195;
imallett

Відповіді:


97

Оскільки питання є подвійним тегом C та C ++, міркування для C ++ та C були б різними:

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

Ось що тут відбувається: лінкер розраховує знайти символ main, і це так. Він "нав'язує" цей символ так, ніби це функція, бо він не знає нічого кращого. Частина бібліотеки часу виконання, яка передає керування mainзапитує лінкер main, тому лінкер надає йому символ main, дозволяючи завершити фазу посилання. Звичайно, це не вдається під час виконання, оскільки mainце не є функцією.

Ось ще одна ілюстрація того ж питання:

файл xc:

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

файл yc:

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

складання:

gcc x.c y.c

Це компілюється, і воно, ймовірно, буде працювати, але це невизначена поведінка, оскільки тип символу, обіцяний компілятору, відрізняється від фактичного символу, що подається в лінкер.

Що стосується попередження, я вважаю, що це розумно: C дозволяє створювати бібліотеки, які не мають mainфункції, тому компілятор звільняє ім'я mainдля інших цілей, якщо вам потрібно визначити змінну mainз незрозумілої причини.


3
Хоча компілятор C ++ розглядає основну функцію по-різному. Його назву не докоряють навіть без зовнішнього "С". Я думаю, це тому, що в іншому випадку для забезпечення зв’язку потрібно буде випромінювати власний зовнішній "C" main.
UldisK

@UldisK Так, я сам це помітив і виявив досить цікавим. Це має сенс, але я ніколи про це не думав.
Джефф Ніксон

2
Насправді результати для C ++ і C не відрізняються, як зазначалося тут, - mainце не підлягає назві манглінгу (так здається) в C ++, незалежно від того, чи це функція чи ні.
Джефф Ніксон

4
@nm Я вважаю, що ваше тлумачення питання занадто вузьке: окрім того, що ставити запитання у назві допису, ОП чітко шукає пояснення, чому його програма, складена в першу чергу ("мій упорядник, здається, так вважає, навіть якщо я цього не роблю "), а також пропозицію, чому це може бути корисно визначити mainяк що-небудь, крім функції. Відповідь пропонує пояснення для обох частин.
dasblinkenlight

1
Те, що основний символ не підпорядковане керуванню іменами, не має значення. У стандарті C ++ не згадується іменоване керування іменами. Ім'я mangling - це питання впровадження.
Девід Хаммен

30

mainНЕ зарезервоване слово це просто зумовлений ідентифікатор (наприклад cin, endl, npos...), так що ви можете оголосити змінну main, форматувати його , а потім роздрукувати його значення.

Звичайно:

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

EDIT

Деякі посилання:

  • main не є зарезервованим словом (C ++ 11):

    Функція mainне повинна використовуватися в межах програми. Зв'язок (3.5) з програми mainвизначається реалізацією. Програма, яка визначає головну як видалену або визнає головним такою inline, що є static, або constexprнеправильно сформована. Ім'я mainне захищено інакше. [Приклад: функції членів, класи та перерахування членів можна викликати main, як і об'єкти в інших просторах імен. - кінцевий приклад]

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

    [2.11 / 3] [...] деякі ідентифікатори зарезервовані для використання реалізаціями C ++ та стандартними бібліотеками (17.6.4.3.2) і не повинні використовуватися інакше; діагностика не потрібна.

    [17.6.4.3.2 / 1] Деякі набори імен та підписів функцій завжди зарезервовані для реалізації:

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

    Зарезервовані слова програміст може не переосмислити, але заздалегідь визначені слова можуть бути замінені в певній якості. Це так main: є сфери, в яких декларація за допомогою цього ідентифікатора переосмислює його значення.


- Я припускаю , що я досить обдурять тим , що (як це буде так схильний до помилок), чому це попередження (не помилка), і чому це тільки попередження , коли скомпільовано як C - Звичайно, ви можете скомпілювати без main()функція, але ви не можете пов'язати його як програму. Тут відбувається те, що "дійсна" програма пов'язана без, а main()просто main.
Джефф Ніксон

7
cinі endlне знаходяться в просторі імен за замовчуванням - вони знаходяться в stdпросторі імен. nposє членом std::basic_string.
ЩеПаркер

1
main буде зарезервований в якості глобального імені. Жодна з інших речей, про які ви згадали, не mainє заздалегідь визначеними.
Potatoswatter

1
Дивіться C ++ 14 §3.6.1 та C11 §5.1.2.2.1 щодо обмежень щодо того, що mainможе бути. C ++ каже: "Реалізація не повинна визначати основну функцію", а C говорить: "Реалізація не оголошує прототипу для цієї функції".
Potatoswatter

@manlio: уточнюйте, з чого ви цитуєте. Щодо звичайного C, цитати неправильні. Тож я гадаю, що це будь-який із стандартів c ++, чи не так?
dhein

19

Чи int main;дійсна програма C / C ++?

Не зовсім зрозуміло, що таке програма C / C ++.

Чи int main;дійсна програма C?

Так. Автономна реалізація може приймати таку програму. mainне повинно мати особливого значення у вільно розташованому середовищі.

Він не дійсний у розміщеному середовищі.

Чи int main;дійсна програма C ++?

Дітто.

Чому вона виходить з ладу?

Програма не повинна мати сенсу у вашому середовищі. У незалежному середовищі запуск та завершення програми, а також значення цього значення mainвизначаються реалізацією.

Чому компілятор попереджає мене?

Компілятор може попередити вас про все, що завгодно, якщо він не відхиляє відповідні програми. З іншого боку, попередження - це все, що потрібно для діагностики невідповідної програми. Оскільки цей блок перекладу не може бути частиною дійсної розміщеної програми, діагностичне повідомлення виправдане.

Це gccвільно розташоване середовище, чи це розміщене середовище?

Так.

gccдокументує -ffreestandingпрапор для складання. Додайте його, і попередження проходить. Ви можете використовувати його під час створення, наприклад, ядер або мікропрограмного забезпечення.

g++не підтверджує такий прапор. Постачання, схоже, не впливає на цю програму. Ймовірно, можна припустити, що середовище, яке надає g ++, розміщене. Відсутність діагностики в цьому випадку - помилка.


17

Це попередження, оскільки технічно заборонено. Стартовий код буде використовувати розташування символу "main" та переходити до нього за допомогою трьох стандартних аргументів (argc, argv та envp). Це не так, і під час зв’язку не можна перевірити, що це насправді функція, навіть навіть, що у них є ці аргументи. Ось чому працює int main (int argc, char ** argv) - компілятор не знає про аргумент envp, і він, як буває, не використовується, і це очищення виклику.

Як жарт, ви могли зробити щось подібне

int main = 0xCBCBCBCB;

на машині x86 і, ігноруючи попередження та подібні речі, він не просто збиратиметься, але і працює.

Хтось застосував подібну методику для написання виконавчого файлу (свого роду), який працює на декількох архітектурах безпосередньо - http://phrack.org/isissue/57/17.html#article . Він також використовувався для перемоги в МОККЦ - http://www.ioccc.org/1984/mullender/mullender.c .


1
"Це попередження, оскільки технічно заборонено" - воно недійсне в C ++.
ура та хт. - Альф

3
"три стандартні аргументи (argc, argv та envp)" - тут, можливо, ви говорите про стандарт Posix.
ура та хт. - Альф

У моїй системі (Ubuntu 14 / x64) наступний рядок працює з gcc:int main __attribute__ ((section (".text")))= 0xC3C3C3C3;
csharpfolk

@ Cheersandhth.-Alf Перші два стандартні, третій - POSIX.
дасканді

9

Це дійсна програма?

Немає.

Це не програма, оскільки вона не має виконуваних частин.

Чи правильно компілювати?

Так.

Чи можна його використовувати з дійсною програмою?

Так.

Не всі складені коди повинні бути виконаними, щоб бути дійсними. Прикладами є статичні та динамічні бібліотеки.

Ви ефективно створили об’єктний файл. Він не є дійсним виконуваним файлом, проте інша програма може посилатися на об'єкт mainу результуючому файлі, завантажуючи його під час виконання.

Чи повинна це бути помилка?

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

Я маю на увазі, що це впевнено, це можна класифікувати як помилку, але чому? Якої мети це могло б служити, що попередження не робить?

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


Він створює зовнішній видимий символ з назвою main. Як справжня програма, яка повинна бути зовні видима функція з ім'ям main, посилання на нього?
Кіт Томпсон

@KeithThompson Завантажте під час виконання. Буде уточнювати.
Майкл Газонда

Це може, тому що він не в змозі визначити різницю між типами символів. Зв'язування працює просто чудово - виконання (за винятком ретельно складеного випадку) не робить.
Кріс Страттон

1
@ChrisStratton: Я думаю, що аргумент Кіта полягає в тому, що зв’язування не вдається, тому що символ визначено багаторазово ... тому що "дійсна програма" не була б дійсною програмою, якщо вона не визначає mainфункцію.
Бен Войгт

@BenVoigt Але якщо воно з’являється в бібліотеці, то посилання не може (і, ймовірно, не може) не вдатися, оскільки в час посилання програми int main;визначення не буде видно.

6

Я хотів би додати відповіді, вже дані, цитуючи фактичні мовні стандарти.

Є '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 Запуск програми

Функція, що викликається при запуску програми, називається основною . [...] Він повинен визначатися з типом повернення int і без параметрів [...], або з двома параметрами [...] або еквівалентом, або яким-небудь іншим способом, визначеним реалізацією.

З цього випливає наступне:

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

У незалежному середовищі виконання я стверджую, що це дійсна програма, яка не дозволяє запускатись, оскільки немає для цього функції, як вимагається в 5.1.2. У розміщеному середовищі виконання, тоді як ваш код вводить об'єкт, названий головним , він не може забезпечити повернене значення, тому я можу стверджувати, що це не є дійсною програмою в цьому сенсі, хоча можна також сперечатися як раніше, якщо програма не є призначені для виконання (на, можливо, захочеться надати дані лише наприклад), тоді це просто не дозволяє робити саме це.

Є 'int main;' дійсна програма C ++?

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

Цитата від C ++ 14

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

Програма повинна містити глобальну функцію, яку називають основною, яка позначається початком програми. Це визначається реалізацією, чи потрібна програма у вільно розташованому середовищі для визначення основної функції. [...] Він повинен мати тип повернення типу int, але в іншому випадку його тип визначається реалізацією. [...] Назва головного не інакше зарезервована.

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

Знову я б заперечував, що для розміщеного випадку ваш код не є дійсною програмою C ++ 14, але я впевнений, що це стосується вільної справи.

Оскільки моя відповідь враховує лише середовище виконання , я думаю, що відповідь dasblinkenlicht вступає в гру, оскільки заздалегідь трапляється ім'я, що виникає в середовищі перекладу . Тут я не такий впевнений, що цитати вище дотримуються настільки суворо.


4

Думаю, я вважаю, що я дійсно думаю, що це повинно бути помилкою в розміщеному середовищі, так?

Помилка твоя. Ви не вказали функцію з назвою, mainяка повертає intта намагалася використовувати вашу програму в розміщеному середовищі.

Припустимо, у вас є одиниця компіляції, яка визначає глобальну змінну з назвою main . Це може бути законним у самостійному середовищі, оскільки те, що становить програму, залишається за реалізацією у вільно розташованих середовищах.

Припустимо, у вас є інший блок компіляції, який визначає глобальну функцію з назвою, mainяка повертає an intі не бере аргументів. Саме цього і потребує програма в розміщеному середовищі.

Все добре, якщо ви використовуєте лише перший компіляційний блок у вільно розташованому середовищі, а другий використовуєте лише в розміщеному середовищі. Що робити, якщо ви використовуєте обидва в одній програмі? В C ++ ви порушили одне правило визначення. Це невизначена поведінка. В C ви порушили правило, яке диктує, що всі посилання на один символ повинні відповідати; якщо вони не - це невизначена поведінка. Невизначена поведінка - це "вийти з в'язниці, вільно!" картка розробникам реалізації. Все, що реалізує у відповідь на невизначене поведінку, відповідає стандарту. Реалізація не повинна попереджати, не кажучи вже про виявлення, невизначеної поведінки.

Що робити, якщо ви використовуєте лише одну з цих компіляційних одиниць, але ви використовуєте неправильну (що саме ви зробили)? У С ситуація чітка. mainНевизначення функції в одній із двох стандартних форм у розміщеному середовищі - це невизначена поведінка. Припустимо, ви взагалі не визначилися main. Компілятор / лінкер нічого не повинен сказати про цю помилку. Те, що вони скаржаться, є вигодою від їхнього імені. У тому, що програма C, складена та пов’язана без помилок, - це ваша вина, а не компілятор.

Це трохи менш зрозуміло в C ++, оскільки невдача у визначенні функції mainв розміщеному середовищі є помилкою, а не невизначеною поведінкою (іншими словами, її потрібно діагностувати). Однак одне правило визначення в C ++ означає, що лінкери можуть бути досить тупими. Завданням лінкера є вирішення зовнішніх посилань, і завдяки одному правилу визначення, лінкер не повинен знати, що означають ці символи. Ви надали символ на ім'я main, лінкер очікує, що він побачить названий символ main, тому все добре, що стосується лінкера.


4

Для C поки це поведінка, визначена реалізацією.

Як говорить ISO / IEC9899:

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

1 Функція, що викликається при запуску програми, називається основною. Реалізація не оголошує прототипу для цієї функції. Він повинен визначатися з типом повернення int та без параметрів:

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

або з двома параметрами (тут іменуються argc та argv, хоча будь-які імена можуть використовуватися, оскільки вони локальні для функції, в якій вони оголошені):

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

або еквівалент; або в інший спосіб, визначений реалізацією.


3

Ні, це не дійсна програма.

Для C ++ це нещодавно було явно зроблено неправильним формуванням звіту про дефекти 1886 р.: Мовна зв'язок для main (), яка говорить:

Здається, не існує жодних обмежень щодо надання явному мовному зв’язку main (), але, мабуть, воно має бути або неправильно сформованим, або умовно підтримуваним.

та частина резолюції включала наступні зміни:

Програма, яка оголошує змінну main в глобальному масштабі або яка оголошує ім'я main зі зв’язком мови C (у будь-якому просторі імен), неправильно сформована.

Ми можемо знайти це формулювання в останньому проекті стандарту C ++ N4527 який є проектом C ++ 1z.

Останні версії clang і gcc тепер роблять цю помилку ( дивіться її в прямому ефірі ):

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

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


Дякуємо за оновлення! Чудово бачити, що зараз це підбирають за допомогою діагностики компілятора. Однак я мушу сказати, що я знаходжу зміни в стандарті C ++, що викликає здивування. (Для перегляду див. Коментарі вище щодо керування іменами main().) Я розумію обґрунтування заборонити main()мати явну специфікацію посилань, але я не розумію, що це обов'язкове main()використання C ++ зв'язку . Звичайно , Стандарт не безпосередньо адреса , як звертатися з ABI зв'язок / ім'я перекручуючи, але на практиці (скажімо, з Itanium ABI) це буде спотворювати main()до _Z4mainv. Що я пропускаю?
Джефф Ніксон

Я думаю , що коментар supercat охоплює це. Якщо реалізація робить своє, перш ніж викликати визначений користувачем головний, то вона може легко вибрати натомість надумане ім'я.
Шафік Ягмур
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.