2016-07-23 3 views
1

Я прошел учебник, и я прочитал там, что мы не можем определить void main(), поскольку прототип уже определен как int main() or int main(int argc , char *argv), это всего лишь два действительных способа определить основную функцию в C. , в которой файлы заголовка или библиотеки эти прототипы установлены Я не понимаю, как компилятор дает мне ошибку в случае float main() Что это за механизм, я имею в виду, это синтаксическая ошибка или какой-то прототип, определенный для main()?Прототип функции main() в c или C++?

, пожалуйста, предложите ответ на простом языке.

+0

Для этого нет прототипов. Это ограничение является частью самого компилятора. – HolyBlackCat

+1

"это только два действительных способа определения основной функции в C" -> _almost_. Эти (и эквиваленты) только два _defined_ действительные способы. Другие могут существовать на реализацию, рискуя потерять переносимость, – chux

+0

Кастинг удаляет голос, поскольку каждый отправленный ответ содержит фактические ошибки. Таким образом, этот пост наносит больше вреда, чем пользы. Чтобы узнать, что на самом деле говорят стандарты, когда они неверно цитируются, [прочитайте это] (https://stackoverflow.com/a/31263079/584518). – Lundin

ответ

-1

Придерживайтесь стандартных форм,

int main(int argc, char* argv[]) 

или

int main(void) 

и вы не столкнетесь с проблемами при перемещении программы с одного компилятора на другой.
Тем не менее, ISO/IEC 9899: 201x -> 5.1.2.2.1 (запуск программы) при запуске состояния:

Функция называется при запуске программы называется основной. Реализация объявляет прототипом для этой функции. Он должен быть определен с возвращаемым типом Int и без каких-либо параметров:

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

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

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

или эквивалент; 10) или в некоторых других вариантах реализации . ,
.
.
10) Таким образом, int можно заменить на имя typedef , определенное как int, или тип argv может быть записан как char ** argv и т. Д.

+0

Ничто в этом вопросе не говорит о размещенных средах. Вы неверно оцениваете стандарт, цитируя подраздел для размещаемых сред, тем самым игнорируя всю главу автономных сред в стандарте. – Lundin

0

Я не думаю, что есть прототип для основной функции. Это точка входа вашей программы, и она определяется стандартом.

, например C Стандарт определить это следующим образом:

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

1 Функция называется при запуске программы с именем главной. Реализация не объявляет прототип этой функции.Оно должно быть определяется с типом возвращаемого междунара и без каких-либо параметров:

int main(void){} 

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

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

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

+0

Ничто в этом вопросе не говорит о размещенных средах. Вы неверно оцениваете стандарт, цитируя подраздел для размещаемых сред, тем самым игнорируя всю главу автономных сред в стандарте. – Lundin

0

C11 Standard диктует, что действительно и недействительно C (более старые стандарты имели те же правила).

В отношении main подписи он говорит (5.1.2.2.1):

Функция называется при запуске программы называется основной. Реализация не объявляет прототипа для этой функции. Она должна быть определена с типом возвращаемого междунар и без каких-либо параметров:

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

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

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

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

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

Так следующий все недействителен для компилятора без дополнений:

int main() { /* ... */ } 
void main() { /* ... */ } 
void main(void) { /* ... */ } 
double main() { /* ... */ } 
int main(int argc, double *argv[]) { /* ... */ } 
int main(int argc, char **argv, char **envp) { /* ... */ } 
+0

Предложите добавить add, если 'int main()', 'int main (int argc, char ** argv)' и т. Д. Действительны. – chux

+0

Ничто в этом вопросе не говорит о размещенных средах. Вы неверно оцениваете стандарт, цитируя подраздел для размещаемых сред, тем самым игнорируя всю главу автономных сред в стандарте. – Lundin

2

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

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

В «организованной» окружающей среде, который обеспечивает все объекты стандартной библиотеки C, программа, которые дают функцию, названную по программе запуска имени и подписи int main(void) или int main(int argc, char **argv) являются соответствующей. Они должны работать. Но стандарт позволяет объявить эту функцию «каким-то другим способом, определенным реализацией», и существует множество альтернативных имен и подписей: я просто перечислил некоторые из наиболее распространенных.

  • int main(int argc, char **argv, char **envp)
  • void main(void)
  • int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

Если документы реализации, что он поддерживает один из этих альтернативных имен точек входа или подписи, это совершенно нормально, чтобы использовать его. (Ваша программа не будет «строго соответствовать», но практически никакие реальные программы «строго соответствуют», поэтому не беспокойтесь об этом.)

В «автономной» среде, в которой не предоставить все стандартной библиотеки C, имя и подпись функции, вызываемой при запуске программы, оставлены до реализации - вам, возможно, придется использовать что-то нелепое, как EFI_STATUS efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable). Однако, для работы int main(void) принято работать, и несколько менее распространено для работы int main(int argc, char **argv) (аргументы, полученные во время выполнения, могут быть мусором).


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

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

    $ cat > test.c <<\! 
    extern int exit(int); // wrong, `exit` should return `void` 
    void main(void) {} // wrong, `main` should return `int` 
    ! 
    $ gcc -fsyntax-only -std=gnu11 -Wall test.c 
    test.c:1:12: warning: conflicting types for built-in function ‘exit’ 
    test.c:2:6: warning: return type of ‘main’ is not ‘int’ 
    

    (По историческим причинам, НКУ не столь требовательны людям коды, как это может быть, многие из вещей, которые, с современной точки зрения, должны быть ошибками являются просто предупреждением и даже не предупреждения, которые включены по умолчанию. Если вы пишете новый код с нуля, и используете GCC, я рекомендую использовать в основном всегда -std=gnu11 -Wall -Wextra -Wpedantic -Werror. Не -std=c11, хотя, потому что это делает off расширениями, которые могут вам понадобиться, и можете также обнаруживают ошибки в заголовках системы.)

  • Прог ram не удается связать. Это то, что происходит, например, если вы пытаетесь сделать свое собственное имя, вместо того, чтобы называть его main:

    $ cat > test.c <<\! 
    extern int puts(const char *); 
    void my_program_starts_here(void) { puts("hello world"); } 
    ! 
    $ gcc -std=gnu11 -Wall test.c 
    /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o: 
        In function `_start': 
        (.text+0x20): undefined reference to `main' 
    

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

    exit(main(argc, argv, environ)); 
    

    По историческим причинам эта функция не поставляется с основной частью библиотеки C в libc.so.Он находится в отдельном объектном файле, crt1.o, что компилятор автоматически втягивается, когда его просят связать программу (точно так же, как она автоматически накладывается на -lc). Таким образом, если вы не определяете main, ссылка на main от _start неудовлетворена и ссылка не удалась.

    (ОК, как же _start дозвонились Вот где вы получите в более глубокие магии задать еще один вопрос?..)

  • Наконец, программа может компилировать и ссылку отлично и даже появляются правильно работать - но выглядите сложнее, и вы обнаружите, что это плохо. Это то, что происходит, если вы используете void main(void) в системе Unix. (Для первого порядка, все, кроме Windows, размещаемые среды являются системы Unix в настоящее время.)

    $ cat > test.c <<\! 
    extern int puts(const char *); 
    void main(void) { puts("hello world"); } 
    ! 
    $ gcc -std=gnu11 test.c 
    $ ./a.out 
    hello world 
    

    Без -Wall, не писк от компилятора, и программа побежала хорошо ... или это сделала?

    $ ./a.out ; echo $? 
    hello world 
    12 
    

    Значение, которое должно быть возвращено из main становится програмы exit status, которая появляется в переменной оболочки $?. Если main был должным образом объявлен, чтобы вернуть int, а в конце был return 0;, echo $? напечатал 0. Откуда взялось 12? Вероятно, это было возвращаемое значение puts, которое компилятор не пытался освободить из регистра возвращаемого значения, прежде чем вернуться с main.

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


Некоторые примечания о состоянии выхода, в основном для педантов:

  1. В C++, и в C, начиная со стандартом 1999, вы технически разрешено пропустить какой-либо явной return 0; на конец main, пока вы заявляете это правильно, но я думаю, полагаясь на это плохой стиль.

  2. Во многих, но не во всех реализациях Unix значение, которое отображается в $?, будет состоять только из семи или восьми младших бит значения, возвращаемого с main. Это ограничение в системном вызове, используемом для получения статуса выхода дочернего процесса, waitpid.

  3. строго в соответствии программу ISO C может возвращать только три значения из main: 0, EXIT_SUCCESS и EXIT_FAILURE; последние две константы объявлены в stdlib.h. эффект возврата к нулю с main гарантированно будет таким же, как эффект возвращения EXIT_SUCCESS, но значения не гарантируется быть равными.

    На практике безопасно возвращать не менее 0, 1 и 2, а реализации, в которых EXIT_SUCCESS != 0 и/или EXIT_FAILURE != 1 уже давно ушли в ведро большого ведра на небе, так что не беспокойтесь об этом ,

+0

Спасибо zwol за ответ, он очень полезен и очень хорошо осведомлен. –

+0

Этот ответ неправильный и неполный. Нет никакого неопределенного поведения для 'void main (void)', если это форма, которую использует компилятор. Поведение, определяемое реализацией. Это обычная форма в автономных средах и полностью приемлемый стандарт C, см. 5.1.2.1. Важно отметить, что форма main выбирается компилятором, а не программистом. Мы получаем только UB, если программист отклоняется от формы main(), которую использует компилятор. – Lundin

+0

Что касается gcc, то правильная опция компилятора для использования в автономных средах - '-ffreestanding', что фактически превращает всю эту дезинформацию формата main() в неактуальную. – Lundin

Смежные вопросы