Оно может компилироваться как угодно - компилятор, а точнее компоновщик, это никак не проверяет, но по стандарту C11 допустимы только эти два варианта. Что будет, если Вы main() объявите по другому не говорится - может работать, а может, в зависимости от расположения пятен на Солнце, например, форматировать диск.
А почему так нельзя делать, потому-что код системной библиотеки, который вызывает Ваш main(), делает это примерно следующим способом:
exit(main(argc, argv));
что примерно соответствует следующему варианту на языке ассемблера (для архитектуры IA-32):
/*вызов основной функции на C*/
call _main
/*завершение программы*/
pushl %eax
call _exit
Как Вы наверное понимаете, при таком вызове, из main()-а ожидают получения кода завершения, который в архитектуре
IA-32 (x86) при соглашениях о вызове
cdecl возвращается через регистр EAX. Если Вы у себя описали void main(void), то, перед выходом из main()-а, этому регистру не будет присвоение какого-нибудь осмысленного значения, и там будет какое-то совершенно случайное значение.
Соответственно и вызов exit() сообщит системе это случайное значение. Далее, программа, которая вызвала Вашу программу через системные вызовы fork()+exec(), через системный вызов wait()/waitpid() получит такой случайный код завершения, и не сможет определить правильно-ли отработала Ваша программа или нет.