Jaka jest właściwa deklaracja głównego?

147

Jaka jest poprawna sygnatura mainfunkcji w C ++? Jaki jest prawidłowy typ zwracanej wartości i co to znaczy zwracać wartość main? Jakie są dozwolone typy parametrów i jakie mają znaczenie?

Czy to jest specyficzne dla systemu? Czy te zasady zmieniały się z czasem? Co się stanie, jeśli je naruszę?

fredoverflow
źródło
1
Jest to bardzo blisko związane z tym, co powinno zostać mainzwrócone w C i C ++ , lub jest jego duplikatem .
Jonathan Leffler
@JonathanLeffler Bez żartów ... został dodany do listy duplikatów w wersji 6 około 8 miesięcy temu.
fredoverflow

Odpowiedzi:

192

mainFunkcja musi być zadeklarowany jako funkcja trzecich w globalnej przestrzeni nazw. Oznacza to, że nie może to być statyczna lub niestatyczna funkcja składowa klasy, ani nie może być umieszczona w przestrzeni nazw (nawet w nienazwanej przestrzeni nazw).

Nazwa mainnie jest zarezerwowana w C ++ z wyjątkiem funkcji w globalnej przestrzeni nazw. Możesz zadeklarować inne wymienione podmiotymain , w tym między innymi klasy, zmienne, wyliczenia, funkcje składowe i funkcje niebędące członkami, które nie znajdują się w globalnej przestrzeni nazw.

Można zadeklarować funkcję nazwaną mainjako funkcja składowa lub w przestrzeni nazw, ale taka funkcja nie byłaby mainfunkcją wyznaczającą miejsce rozpoczęcia programu.

mainFunkcja nie może być uznana jako staticlub inline. Nie można go również przeciążać; mainw globalnej przestrzeni nazw może istnieć tylko jedna funkcja .

mainFunkcja nie może być używana w programie: nie wolno wywołać mainfunkcję z dowolnego miejsca w kodzie, ani ty mogą brać swój adres.

Zwracany typ main musi byćint . Żaden inny typ zwracania nie jest dozwolony (ta reguła jest pogrubiona, ponieważ bardzo często można zobaczyć niepoprawne programy, które deklarują mainzwracany typ void; jest to prawdopodobnie najczęściej naruszana reguła dotycząca mainfunkcji).

Istnieją dwie deklaracje tego, mainco musi być dozwolone:

int main()               // (1)
int main(int, char*[])   // (2)

W (1) nie ma parametrów.

W (2) są dwa parametry i są one umownie nazwaneargc i argv, odpowiednio. argvjest wskaźnikiem do tablicy ciągów C reprezentujących argumenty programu. argcto liczba argumentów w argvtablicy.

Zwykle argv[0]zawiera nazwę programu, ale nie zawsze tak jest. argv[argc]na pewno jest pustym wskaźnikiem.

Zauważ, że ponieważ argument typu tablicowego (jak char*[]) jest w rzeczywistości tylko argumentem typu wskaźnika w przebraniu, poniższe dwa są poprawnymi sposobami zapisu (2) i oba oznaczają dokładnie to samo:

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

Niektóre implementacje mogą dopuszczać inne typy i liczby parametrów; musiałbyś sprawdzić dokumentację swojej implementacji, aby zobaczyć, co obsługuje.

main()oczekuje, że zwróci zero, aby wskazać sukces i niezerowe, aby wskazać błąd. Nie musisz jawnie pisać returninstrukcji w main(): jeśli pozwalasz na main()powrót bez wyraźnego returnoświadczenia, jest to tak samo, jak gdybyś to napisał return 0;. Następujące dwie main()funkcje mają takie samo zachowanie:

int main() { }
int main() { return 0; }

Istnieją dwa makra, EXIT_SUCCESSa EXIT_FAILUREzdefiniowane w nich <cstdlib>mogą być również zwracane zmain() aby wskazać odpowiednio sukces i niepowodzenie.

Wartość zwracana przez main()jest przekazywana doexit() funkcji, która kończy działanie programu.

Zauważ, że wszystko to dotyczy tylko kompilacji dla środowiska hostowanego (nieformalnie, środowiska, w którym masz pełną bibliotekę standardową i system operacyjny, na którym działa twój program). Możliwe jest również skompilowanie programu w C ++ dla środowiska wolnostojącego (na przykład niektórych typów systemów wbudowanych), w którym to przypadku uruchomienie i zakończenie są całkowicie zdefiniowane przez implementację, a main()funkcja może nawet nie być wymagana. Jeśli jednak piszesz C ++ dla nowoczesnego systemu operacyjnego dla komputerów stacjonarnych, kompilujesz dla środowiska hostowanego.

James McNellis
źródło
1
IIRC jedynymi gwarantowanymi wartościami zwracanymi są 0, EXIT_SUCCESS (taki sam efekt jak 0) i EXIT_FAILURE. EDYCJA: Ah, OK, mogą zostać zwrócone inne niezerowe wartości statusu, ale o znaczeniu zdefiniowanym przez implementację. Tylko EXIT_FAILURE jest w jakiś sposób interpretowane jako wartość błędu.
Derrick Turk
4
@Synetech: Pytanie w pierwszym zdaniu brzmi: „Jaka jest poprawna sygnatura funkcji głównej w C ++?” a pytanie jest oznaczone jako [c ++] i [c ++ - faq]. Nic na to nie poradzę, jeśli użytkownicy Javy lub C # (lub ktokolwiek inny) nadal są zdezorientowani. C # musi Mainbyć statyczną funkcją składową, ponieważ nie ma nawet funkcji niebędących członkami. Nawet C89 wymaga mainpowrotu int. Nie jestem wystarczająco zaznajomiony z K&R C, aby poznać jego dokładne reguły, ale myślę, że wymaga również mainpowrotu, intponieważ mainbez zwracanego typu był dość powszechny i ​​brak type = implicit intw K&R.
James McNellis,
3
@Suhail: Ponieważ standard języka mówi, że typem zwrotu będzie int.
James McNellis
1
@Suhail: Tak. Twój kod nie będzie poprawny C ++ i wiele kompilatorów odrzuci Twój kod.
James McNellis
2
@Suhail: Visual C ++ zezwala na voidzwracany typ jako rozszerzenie języka . Kompilatory, które na to nie pozwalają, obejmują GCC i Comeau.
James McNellis
15

Z dokumentacji standardowej, 3.6.1.2 Główna funkcja ,

Powinien mieć zwracany typ typu int, ale poza tym jego typ jest zdefiniowany w implementacji. Wszystkie implementacje pozwalają na obie z następujących definicji main:

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

W tej ostatniej postaci argcpodaje się liczbę argumentów przekazanych do programu ze środowiska, w którym program jest uruchamiany. Jeśli argument argc jest różny od zera, argumenty te należy podać w argv [0] do argv [argc-1] jako wskaźniki do początkowego znaki wielobajtowych łańcuchów zakończonych znakiem null .....

Mam nadzieję, że to pomoże ...

liaK
źródło
2
czy istnieje jakiś konkretny powód, dla którego mainpowinien być zwracany typ int?
Suhail Gupta
1
@SuhailGupta: Aby proces wywoływania wiedział, czy ten proces powinien zostać uznany za udany, czy nie. Pozwolenie voidzrywa ten model. To nawet nie ma sensu, gdybyś miał na myśli „zawsze uważaj za sukces”. Ponieważ nie można było powiedzieć, czy proces się nie powiódł, więc czy naprawdę się udało? Nie, wróć int.
Wyścigi lekkości na orbicie
4

Dokładne brzmienie najnowszego opublikowanego standardu (C ++ 14) to:

Wdrożenie powinno umożliwiać jedno i drugie

  • funkcja ()zwracania inti

  • funkcja (int, wskaźnik do wskaźnika do char)powrotuint

jako typ main.

To wyjaśnia, że ​​alternatywne pisownie są dozwolone, o ile typem mainjest typ int()lub int(int, char**). Zatem dozwolone są również:

  • int main(void)
  • auto main() -> int
  • int main ( )
  • signed int main()
  • typedef char **a; typedef int b, e; e main(b d, a c)
MM
źródło
1
NB. Opublikowałem tę odpowiedź tak, jak w komentarzach do innego wątku, ktoś próbował cytować ten wątek jako dowód, który int main(void)nie był poprawny w C ++.
MM
3
@Stargateur auto main() -> intnie ma wydedukowanego typu zwrotu. Zwróć uwagę na {in "(auto main () {... is not allowed)" i dowiedz się, kiedy nie wiesz jeszcze wystarczająco dużo, aby dodać cokolwiek sensownego.
3

Dwie ważne sieci to int main()i int main(int, char*[]). Każda inna rzecz może się skompilować lub nie. Jeśli mainnie zwraca jawnie wartości, niejawnie zwracane jest 0 .

kamień kamienny
źródło
1
Nigdy nie widziałem, aby kod nie był kompilowany, gdy wspominam o zwracanym typie, mainktóry ma być void. Czy jest jakiś konkretny powód, dla którego typem zwracanym main powinien być int?
Suhail Gupta
4
Specyfikacja języka mówi, że main musi mieć zwracany typ int. Każdy inny typ zwracania dozwolony przez kompilator jest ulepszeniem specyficznym dla kompilatora. Zasadniczo używanie void oznacza, że ​​programujesz w języku podobnym do C ++, ale nie.
kamień kamieniarski
2
Powodem, dla którego standard wymaga an intjako zwracanego typu mainjest to, że ta wartość jest przekazywana powłoce jako kod zakończenia programu i shoczekuje int.
uckelman
Może powodem jest dyscyplina? Może być więcej niż jedna droga wyjścia. Jeśli typ zwracany to void, wszystkie są ciche. Z tym intmusimy zdefiniować konkretną wartość wyjścia dla każdego powrotu z main.
Andreas Spindler
2

Szczegóły dotyczące zwracanych wartości i ich znaczenia

Na 3.6.1 ( [basic.start.main]):

Instrukcja return in mainskutkuje opuszczeniem mainfunkcji (zniszczeniem wszystkich obiektów z automatycznym czasem trwania) i wywołaniem std::exitz wartością zwracaną jako argumentem. Jeśli kontrola osiągnie koniec mainbez napotkania returninstrukcji, efektem jest wykonanie

return 0;

Zachowanie std::exitjest szczegółowo opisane w sekcji 18.5 ( [support.start.term]) i opisuje kod stanu:

Wreszcie kontrola jest zwracana do środowiska hosta. Jeśli status ma wartość zero lub EXIT_SUCCESS, zwracana jest zdefiniowana w implementacji forma pomyślnego zakończenia statusu. Jeśli status to EXIT_FAILURE, zwracana jest zdefiniowana w implementacji forma statusu niepomyślne zakończenie. W przeciwnym razie zwracany stan jest zdefiniowany w ramach implementacji.

Ben Voigt
źródło