Co powinien zwrócić main () w C i C ++?


695

28
Nadal uważam, że to dość niejasne. Zdefiniuj dla mnie „najbardziej wydajny”. Wydajny w jakim sensie? W sensie zajmowania mniej pamięci? W sensie szybszego biegania? Widzę przydatne odpowiedzi, ale nadal uważam, że pytanie jest dość źle sformułowane.
Onorio Catenacci

7
Pish posh, kontekst wydajnego jest tutaj oczywisty, szczególnie w przykładach (które prawdopodobnie wyjaśnią definicję „wydajnego”). Mam nadzieję, że słaby bufor nie wpełzł do dziury i żałuje tego pytania całkowicie. Można powiedzieć, że niezależnie od void lub int zwracana jest wartość, więc nie ma ona wpływu na rozmiar pliku, wykonywane operacje ani przydzieloną pamięć. Ludzie w większości systemów operacyjnych zwracają 0 po sukcesie, a coś innego - innym - sukcesie lub porażce - ale nie ma standardu. Ostatecznie nie ma różnicy w wydajności w żaden oczywisty sposób.
Kit10

„poprawne (najbardziej wydajne)” nie ma sensu. Wydajność to jedno, poprawne to drugie. mainjest wywoływany raz (aw C ++ można wywoływać tylko raz: brak rekurencji). Jeśli nie chcesz, aby wykonywanie spędzało dużo czasu main, nie wywoływaj programu wiele razy: spraw, aby program zaimplementował powtórzenie.
Kaz

2
Interesujące jest dla mnie to, że żadna z odpowiedzi, o ile mogę powiedzieć, nie stanowi w pełni działającego przykładu, w tym #includeoświadczeń
puk 11.11.13

3
Zwracane wartości nie mają sensu na platformie bez systemu operacyjnego. Do niczego nie wracasz. Jeśli trafisz returnw main(...)na wbudowanym urządzeniu, system przechodzi w stan nieprzewidywalny i pralka będzie świadomym i próbować cię zabić. Tak więc używamy void main()w tym przypadku. Jest to standardowa praktyka przemysłowa w osadzaniu goły metal.
3Dave

Odpowiedzi:


570

Wartość zwracana dla mainwskazuje, jak program zakończył pracę. Normalne wyjście jest reprezentowane przez 0 zwracaną wartość z main. Nieprawidłowe wyjście jest sygnalizowane zwrotem niezerowym, ale nie ma standardu interpretacji kodów niezerowych. Jak zauważają inni, void main()jest to zabronione przez standard C ++ i nie powinno się go używać. Prawidłowe mainpodpisy w C ++ to:

int main()

i

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

co jest równoważne z

int main(int argc, char** argv)

Warto również zauważyć, że w C ++ int main()można pozostawić bez instrukcji return, w którym to momencie domyślnie zwraca 0. To samo dotyczy programu C99. To, czy return 0;należy je pominąć, czy nie, jest przedmiotem dyskusji. Zakres prawidłowych sygnatur głównych programu C jest znacznie większy.

Wydajność nie stanowi problemu z mainfunkcją. Można go wprowadzić i zostawić tylko raz (oznaczając początek i zakończenie programu) zgodnie ze standardem C ++. W przypadku C ponowne wejście main()jest dozwolone, ale należy go unikać.


69
main MOŻNA wejść / wyjść wiele razy, ale ten program prawdopodobnie nie wygrałby żadnych nagród za projekt;)
korona

13
C99 ma również błąd C ++, że osiągnięcie końca funkcji main () jest równoważne zwróceniu wartości 0 - jeśli zdefiniowano main (), aby zwrócić typ zgodny z int (sekcja 5.1.2.2.3).
Jonathan Leffler

62
ponowne wprowadzenie main nie jest poprawne C ++. Jawnie w standardzie 3.6.1.3 stwierdza: „main nie powinien być używany w programie”
workmad3

117
stdlib.h zapewnia w tym celu EXIT_SUCCESS i EXIT_FAILURE
Clay

20
0 i niezerowe są poprawne, ale zupełnie bez znaczenia dla osoby czytającej Twój kod. To pytanie jest dowodem, że ludzie nie wiedzą, jakie są prawidłowe / nieprawidłowe kody. EXIT_SUCCESS / EXIT_FAILURE są znacznie wyraźniejsze.
JaredPar

169

Akceptowana odpowiedź wydaje się być skierowana do C ++, więc pomyślałem, że dodam odpowiedź dotyczącą C, a to różni się na kilka sposobów.

ISO / IEC 9899: 1989 (C90):

main() należy zadeklarować jako:

int main(void)
int main(int argc, char **argv)

Lub odpowiednik. Na przykład int main(int argc, char *argv[])jest równoważny z drugim. Ponadto inttyp zwrotu można pominąć, ponieważ jest domyślny.

Jeśli implementacja na to pozwala, main()może być zadeklarowana na inne sposoby, ale powoduje to, że implementacja programu jest zdefiniowana i nie jest już ściśle zgodna.

Norma definiuje 3 wartości do zwracania, które są ściśle zgodne (tzn. Nie polegają na zachowaniu zdefiniowanym w ramach implementacji): 0oraz EXIT_SUCCESSdla pomyślnego zakończenia i EXIT_FAILUREnieudanego zakończenia. Wszelkie inne wartości są niestandardowe, a implementacja zdefiniowana. na końcu main()musi zawierać wyraźne returnoświadczenie, aby uniknąć niezdefiniowanego zachowania.

Wreszcie ze standardowego punktu widzenia nie ma nic złego w wywoływaniu main()z programu.

ISO / IEC 9899: 1999 (C99):

W przypadku C99 wszystko jest takie samo jak powyżej, z wyjątkiem:

  • Typ intzwrotu nie może zostać pominięty.
  • Możesz pominąć instrukcję return z main(). Jeśli to zrobisz i main()skończysz, istnieje domniemanie return 0.

1
@Lundin Nie sądzę, żebyś potrzebował cytatu, aby powiedzieć, że ktoś może stworzyć kompilator, który akceptuje programy niezgodne ze standardami, lub mieć kompilator niezgodny ze standardami. To powszechna wiedza i zdrowy rozsądek
KABoissonneault,

4
@KABoissonneault Zachowanie zdefiniowane w implementacji jest terminem standardowym, w przeciwieństwie do zachowania całkowicie nieudokumentowanego. Jeśli implementujesz coś, co jest wymienione jako zachowanie zdefiniowane w ramach implementacji, nadal postępujesz zgodnie ze standardem. W tym przypadku C89, który został zacytowany, nie wymienia takiego zachowania zdefiniowanego w implementacji, stąd potrzeba cytowania, aby udowodnić, że nie tylko wymyśla coś nieoczekiwanego.
Lundin

1
@Lundin Widzisz to w niewłaściwy sposób. To, o czym mówimy, nie jest zachowaniem zdefiniowanym w implementacji, mówimy o implementacji odbiegającej od standardu, jeśli tak wybiorą. To bardziej jak dziecko nieposłuszne rodzicom: nie potrzebujesz cytatu od rodziców, aby powiedzieć ci, w jaki sposób dziecko może postąpić wbrew temu, co powiedzieli rodzice. Po prostu wiesz, że w chwili, gdy dziecko zdecyduje się to zrobić, przestają być zgodne z zasadami
gildii

2
@KABoissonneault Część, którą zacytowałem w moim komentarzu, zdecydowanie dotyczy zachowań zdefiniowanych w implementacji (w przeciwieństwie do niestandardowych rozszerzeń kompilatora ). Dlatego mówię o zachowaniu zdefiniowanym w implementacji. Jeśli masz monolog o czymś innym, powodzenia.
Lundin

1
@Lundin Wydaje mi się, że sformułowanie w cytacie jest mylące (część, w której mówią „ale to definiuje implementację programu”), ale jestem pewien, że dana osoba mówiła o niestandardowym zachowaniu (jak napisano w „Jeśli implementacja zezwala na to „i” i nie jest już ściśle zgodny [ze standardem] ”), w przeciwieństwie do rzeczywistych zachowań zdefiniowanych dla implementacji. Osoba powinna zdecydowanie przeredagować swoją odpowiedź, ale nadal nie sądzę, aby cytat ze standardu był w tym zakresie niezbędny
KABoissonneault

117

Standard C - środowisko hostowane

W przypadku hostowanego środowiska (to jest normalne) standard C11 (ISO / IEC 9899: 2011) mówi:

5.1.2.2.1 Uruchomienie programu

Nazwana jest funkcja wywoływana podczas uruchamiania programu main. Implementacja deklaruje brak prototypu dla tej funkcji. Należy go zdefiniować za pomocą typu zwrotu inti bez parametrów:

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

lub z dwoma parametrami (określanymi tutaj jako argci argvchociaż można używać dowolnych nazw, ponieważ są one lokalne dla funkcji, w której zostały zadeklarowane):

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

lub odpowiednik; 10) lub w inny określony sposób.

Jeżeli zostaną zadeklarowane, parametry funkcji głównej muszą spełniać następujące ograniczenia:

  • Wartość argcmusi być nieujemna.
  • argv[argc] będzie wskaźnikiem zerowym.
  • Jeśli wartość argcjest większa od zera, elementy tablicy argv[0]poprzez argv[argc-1]włącznie zawierają wskaźniki do łańcuchów, którym środowisko hosta otrzymuje wartości zdefiniowane w implementacji przed uruchomieniem programu. Celem jest dostarczenie do informacji o programie określonych przed uruchomieniem programu z innego miejsca w hostowanym środowisku. Jeśli środowisko hosta nie jest w stanie dostarczyć ciągów liter zarówno wielkimi, jak i małymi, wdrożenie zapewnia, że ​​ciągi są odbierane małymi literami.
  • Jeśli wartość argcjest większa od zera, ciąg wskazany przez argv[0] oznacza nazwę programu; argv[0][0]będzie znakiem pustym, jeśli nazwa programu nie jest dostępna w środowisku hosta. Jeśli wartość argcjest większa niż jeden, ciągi wskazywane przez argv[1]przez argv[argc-1] reprezentują parametry programu.
  • Parametry argci argvciągi wskazane przez argvtablicę mogą być modyfikowane przez program i zachowują swoje ostatnio zapisane wartości między uruchomieniem programu a zakończeniem programu.

10) Tak więc intmożna go zastąpić nazwą typu zdefiniowanego jako intlub typ argvmożna zapisać jako char **argvi tak dalej.

Zakończenie programu w C99 lub C11

Zwrócona wartość main()jest przekazywana do „środowiska” w sposób zdefiniowany w implementacji.

5.1.2.2.3 Zakończenie programu

1 Jeśli typ zwracany przez mainfunkcję jest typem zgodnym int, powrót z początkowego wywołania mainfunkcji jest równoważny wywołaniu exitfunkcji z wartością zwróconą przez mainfunkcję jako argument; 11) osiągnięcie wartości }kończącej mainfunkcję powoduje zwrócenie wartości 0. Jeśli typ zwracany jest niezgodny int, status zakończenia zwracany do środowiska hosta jest nieokreślony.

11) Zgodnie z 6.2.4, czasy życia obiektów z zadeklarowanym automatycznym czasem przechowywania main zostaną zakończone w pierwszym przypadku, nawet jeśli nie miałyby miejsca w drugim przypadku.

Pamiętaj, że 0jest to obowiązkowe jako „sukces”. Możesz używać EXIT_FAILUREi EXIT_SUCCESSod<stdlib.h> jeśli wolisz, ale 0 jest dobrze rozwinięta, a więc jest 1. Patrz także zjazdu kody większa niż 255 - możliwe? .

W C89 (i stąd w Microsoft C) nie ma informacji o tym, co się stanie, jeśli main() funkcja zwróci, ale nie określi wartości zwracanej; prowadzi to zatem do nieokreślonego zachowania.

7.22.4.4 exitFunkcja

5 Wreszcie kontrola jest zwracana do środowiska hosta. Jeśli wartość statuswynosi zero lub EXIT_SUCCESS, zwracana jest zdefiniowana w implementacji forma pomyślnego zakończenia statusu . Jeśli wartością statusjest EXIT_FAILURE, zdefiniowana w implementacji forma statusu nieudanego zakończenia . W przeciwnym razie zwracany status jest zdefiniowany w implementacji.

Standard C ++ - środowisko hostowane

Norma C ++ 11 (ISO / IEC 14882: 2011) mówi:

3.6.1 Główna funkcja [basic.start.main]

1 Program powinien zawierać funkcję globalną o nazwie main, która jest wyznaczonym początkiem programu. [...]

2 Implementacja nie powinna predefiniować głównej funkcji. Ta funkcja nie może być przeciążona. Powinien mieć typ zwracany typu int, ale w przeciwnym razie jego typ jest zdefiniowany jako implementacja. Wszystkie implementacje powinny umożliwiać obie następujące definicje głównej:

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

i

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

W tej ostatniej formie argcpowinna być liczba argumentów przekazanych do programu ze środowiska, w którym program jest uruchomiony. Jeśli argcjest niezerowe argumenty te powinny być dostarczane w argv[0] przez argv[argc-1]jako wskaźniki do początkowych znaków NUL ciągów wielobajtowych (NTMBSs) (17.5.2.1.4.2) i argv[0]powinien być wskaźnik do pierwotnego charakteru a NTMBS który reprezentuje nazwę używaną do wywołać program lub "". Wartość argcmusi być nieujemna. Wartość argv[argc] powinna wynosić 0. [Uwaga: Zaleca się późniejsze dodanie (opcjonalnych) parametrów argv. —Wskazówka]

¶3 Funkcji mainnie należy używać w programie. Połączenie (3.5) mainjest zdefiniowane w implementacji. [...]

5 Polecenie return w main powoduje pozostawienie funkcji main (zniszczenie dowolnych obiektów z automatycznym czasem przechowywania) i wywołanie std::exitz wartością zwracaną jako argumentem. Jeśli kontrola osiągnie koniec Main bez napotkania instrukcji return, efektem jest wykonanie

return 0;

Standard C ++ wyraźnie mówi: „To [główna funkcja] powinna mieć typ zwracany typu int, ale poza tym jego typ jest zdefiniowany w implementacji”, i wymaga tych samych dwóch sygnatur co standard C, aby był obsługiwany jako opcje. Zatem „void main ()” nie jest bezpośrednio dozwolone przez standard C ++, chociaż nic nie może zrobić, aby zatrzymać niestandardową implementację dopuszczającą alternatywy. Zauważ, że C ++ zabrania użytkownikowi dzwonienia main(ale standard C nie).

W standardzie C ++ 11 znajduje się akapit §18.5 Rozpoczęcie i zakończenie, który jest identyczny z akapitem z § 7.22.4.4 . exitFunkcja w standardzie C11 (cytowana powyżej), oprócz przypisu (który po prostu dokumentuje to EXIT_SUCCESSi EXIT_FAILUREjest zdefiniowana w <cstdlib>).

Standard C - Wspólne rozszerzenie

Klasycznie systemy Unix obsługują trzeci wariant:

int main(int argc, char **argv, char **envp) { ... }

Trzeci argument to zakończona zerem lista wskaźników do ciągów, z których każdy jest zmienną środowiskową, która ma nazwę, znak równości i wartość (być może pustą). Jeśli tego nie użyjesz, nadal możesz dostać się do środowiska poprzez ' extern char **environ;'. Ta zmienna globalna jest unikalna wśród tych w POSIX, ponieważ nie ma nagłówka, który ją deklaruje.

Jest to uznawane przez normę C za wspólne rozszerzenie, udokumentowane w załączniku J:

J.5.1 Argumenty środowiskowe

¶1 W środowisku hostowanym funkcja główna otrzymuje trzeci argument char *envp[]wskazujący na zakończoną zerem tablicę wskaźników char, z których każdy wskazuje na ciąg znaków informujący o środowisku dla wykonania programu (5.1. 2.2.1).

Microsoft C.

Microsoft VS 2010 kompilator jest interesująca. Strona internetowa mówi:

Składnia deklaracji dla main to

 int main();

lub opcjonalnie

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

Alternatywnie funkcje maini wmainmożna zadeklarować jako zwracane void(bez wartości zwracanej). Jeśli zadeklarujesz mainlub wmainanulujesz, nie możesz zwrócić kodu wyjścia do procesu nadrzędnego lub systemu operacyjnego za pomocą instrukcji return. Aby zwrócić kod wyjścia, gdy mainlub wmainjest zadeklarowany jako void, musisz użyć exitfunkcji.

Nie jest dla mnie jasne, co się dzieje (jaki kod wyjścia jest zwracany do rodzica lub systemu operacyjnego), gdy program ma void main() robi wyjście - a strona MS również milczy.

Co ciekawe, MS nie zaleca dwuargumentowej wersji main()wymaganej przez standardy C i C ++. Określa tylko trzyargumentową formę, w której znajduje się trzeci argumentchar **envp wskaźnikiem do listy zmiennych środowiskowych.

Strona Microsoft wymienia także kilka innych alternatyw - wmain()które wymagają szerokich ciągów znaków i kilka innych.

Wersja tej strony programu Microsoft Visual Studio 2005 nie jest wymieniona jako alternatywa. W wersjach z Microsoft Visual Studio 2008 r robić.void main()

Standard C - środowisko wolnostojące

Jak wspomniano wcześniej, powyższe wymagania dotyczą środowisk hostowanych. Jeśli pracujesz w środowisku wolnostojącym (które jest alternatywą dla środowiska hostowanego), standard ma znacznie mniej do powiedzenia. W środowisku wolnostojącym funkcja wywoływana podczas uruchamiania programu nie musi być wywoływana maini nie ma żadnych ograniczeń co do typu jej powrotu. Standard mówi:

5.1.2 Środowiska wykonawcze

Zdefiniowano dwa środowiska wykonawcze: wolnostojące i hostowane. W obu przypadkach uruchomienie programu następuje, gdy środowisko wykonawcze wywołuje wyznaczoną funkcję C. Wszystkie obiekty ze statycznym czasem przechowywania powinny zostać zainicjowane (ustawione na wartości początkowe) przed uruchomieniem programu. Sposób i czas takiej inicjalizacji są poza tym nieokreślone. Zakończenie programu zwraca kontrolę do środowiska wykonawczego.

5.1.2.1 Środowisko wolnostojące

W środowisku wolnostojącym (w którym wykonywanie programu C może odbywać się bez jakiejkolwiek korzyści systemu operacyjnego), nazwa i typ funkcji wywoływanej podczas uruchamiania programu są zdefiniowane pod względem implementacji. Wszelkie funkcje biblioteczne dostępne dla programu wolnostojącego, inne niż minimalny zestaw wymagany przez klauzulę 4, są zdefiniowane w implementacji.

Efekt zakończenia programu w środowisku wolnostojącym jest definiowany pod względem implementacji.

Odniesienie do klauzuli 4 Zgodność odnosi się do tego:

5 Program ściśle zgodny powinien używać tylko tych funkcji języka i biblioteki określonych w niniejszej Normie Międzynarodowej. 3) Nie będzie generować wyników zależnych od jakiegokolwiek nieokreślonego, niezdefiniowanego lub określonego dla implementacji zachowania i nie przekroczy żadnego minimalnego limitu implementacji.

¶6 Dwie formy implementacji zgodnej są hostowane i wolnostojące . Zgodne gospodarzem realizacja powinna przyjąć dowolny program ściśle zgodny. Wykonanie zgodne wolnostojące przyjmuje dowolny program ściśle zgodny, w których korzystanie z funkcji określonych w klauzuli biblioteki (klauzula 7) ogranicza się do zawartości standardowych nagłówków <float.h>, <iso646.h>, <limits.h>, <stdalign.h>, <stdarg.h>, <stdbool.h>, <stddef.h>, <stdint.h>, i <stdnoreturn.h>. Implementacja zgodna może mieć rozszerzenia (w tym dodatkowe funkcje biblioteczne), pod warunkiem, że nie zmieniają zachowania żadnego ściśle zgodnego programu. 4)

7 Program zgodny jest programem akceptowanym przez implementację zgodną. 5)

3) Program ściśle zgodny może korzystać z funkcji warunkowych (patrz 6.10.8.3), pod warunkiem, że użycie jest chronione przez odpowiednią dyrektywę wstępnego przetwarzania warunkowego włączenia z wykorzystaniem powiązanego makra. Na przykład:

#ifdef __STDC_IEC_559__ /* FE_UPWARD defined */
    /* ... */
    fesetround(FE_UPWARD);
    /* ... */
#endif

4) Oznacza to, że zgodne wdrożenie nie zastrzega żadnych identyfikatorów innych niż te wyraźnie zastrzeżone w niniejszej Normie Międzynarodowej.

5) Programy ściśle zgodne mają być maksymalnie przenośne wśród zgodnych implementacji. Zgodne programy mogą zależeć od nieprzenośnych funkcji zgodnej implementacji.

Można zauważyć, że jedynym wymaganym nagłówkiem wolnostojącego środowiska, które faktycznie definiuje dowolne funkcje, jest <stdarg.h> (a nawet te mogą - i często są - tylko makra).

Standard C ++ - środowisko wolnostojące

Podobnie jak standard C rozpoznaje zarówno środowisko hostowane, jak i wolnostojące, podobnie standard C ++. (Cytaty z ISO / IEC 14882: 2011.)

1.4 Zgodność z implementacją [wstęp. Zgodność]

¶7 Zdefiniowano dwa rodzaje implementacji: implementację hostowaną i implementację wolnostojącą . W przypadku hostowanej implementacji ten standard międzynarodowy określa zestaw dostępnych bibliotek. Wolnostojąca implementacja to taka, w której wykonanie może odbywać się bez korzyści systemu operacyjnego, i zawiera zdefiniowany przez implementację zestaw bibliotek, który zawiera pewne biblioteki obsługujące język (17.6.1.3).

8 Implementacja zgodna może mieć rozszerzenia (w tym dodatkowe funkcje biblioteczne), pod warunkiem, że nie zmieniają zachowania żadnego poprawnie sformułowanego programu. W celu zdiagnozowania programów korzystających z takich rozszerzeń, które są źle sformułowane zgodnie z niniejszym standardem międzynarodowym, wymagane są implementacje. Po wykonaniu tych czynności mogą jednak kompilować i uruchamiać takie programy.

9 Każda implementacja powinna zawierać dokumentację, która identyfikuje wszystkie konstrukcje wspierane warunkowo, których nie obsługuje i definiuje wszystkie cechy charakterystyczne dla ustawień regionalnych. 3)

3) Ta dokumentacja określa również zachowanie zdefiniowane w ramach implementacji; patrz 1.9.

17.6.1.3 Wdrożenia wolnostojące [zgodność]

Zdefiniowano dwa rodzaje implementacji: hostowany i wolnostojący (1.4). W przypadku hostowanej implementacji ten Międzynarodowy Standard opisuje zestaw dostępnych nagłówków.

Wolnostojąca implementacja ma zdefiniowany zestaw implementacji nagłówków. Zestaw ten zawiera co najmniej nagłówki przedstawione w tabeli 16.

Dostarczone wersja nagłówku <cstdlib>deklarują przynajmniej funkcje abort, atexit, at_quick_exit, exit, i quick_exit(18.5). Pozostałe nagłówki wymienione w tej tabeli muszą spełniać te same wymagania, co w przypadku hostowanej implementacji.

Tabela 16 - Nagłówki C ++ dla implementacji wolnostojących

Subclause                           Header(s)
                                    <ciso646>
18.2  Types                         <cstddef>
18.3  Implementation properties     <cfloat> <limits> <climits>
18.4  Integer types                 <cstdint>
18.5  Start and termination         <cstdlib>
18.6  Dynamic memory management     <new>
18.7  Type identification           <typeinfo>
18.8  Exception handling            <exception>
18.9  Initializer lists             <initializer_list>
18.10 Other runtime support         <cstdalign> <cstdarg> <cstdbool>
20.9  Type traits                   <type_traits>
29    Atomics                       <atomic>

Co z używaniem int main()w C?

Norma §5.1.2.2.1 normy C11 pokazuje preferowaną notację -  int main(void)- ale są też dwa przykłady w normie, które pokazują int main(): §6.5.3.4 ¶8 i §6.7.6.3 ¶20 . Należy teraz zauważyć, że przykłady nie są „normatywne”; są tylko przykładowe. Jeśli w przykładach występują błędy, nie wpływają one bezpośrednio na główny tekst standardu. To powiedziawszy, mocno wskazują na oczekiwane zachowanie, więc jeśli standard zawiera int main()w przykładzie, sugeruje, że int main()nie jest to zabronione, nawet jeśli nie jest to preferowany zapis.

6.5.3.4 Operatorzy sizeofi_Alignof

8 PRZYKŁAD 3 W tym przykładzie wielkość tablicy o zmiennej długości jest obliczana i zwracana z funkcji:

#include <stddef.h>

size_t fsize3(int n)
{
    char b[n+3]; // variable length array
    return sizeof b; // execution time sizeof
}
int main()
{
    size_t size;
    size = fsize3(10); // fsize3 returns 13
    return 0;
}

@DavidBowling: Definicja funkcji jak int main(){ … }określa, że ​​funkcja nie przyjmuje argumentów, ale nie zapewnia prototypu funkcji AFAICT. Na main()to rzadko problem; oznacza to, że jeśli masz rekurencyjne wywołania main(), argumenty nie zostaną sprawdzone. W przypadku innych funkcji jest to bardziej problem - naprawdę potrzebujesz prototypu zasięgu, gdy funkcja jest wywoływana, aby upewnić się, że argumenty są poprawne.
Jonathan Leffler,

1
@DavidBowling: Zwykle nie dzwonisz main()rekurencyjnie poza miejscami takimi jak IOCCC. Mam program testowy, który to robi - głównie pod kątem nowości. Jeśli masz int i = 0; int main() { if (i++ < 10) main(i, i * i); return 0; }i kompilujesz za pomocą GCC, a nie zawierasz -Wstrict-prototypes, kompiluje się czysto pod surowymi ostrzeżeniami. Jeśli tak main(void), nie można go skompilować.
Jonathan Leffler,

61

Uważam, że main()powinien wrócić albo EXIT_SUCCESSalbo EXIT_FAILURE. Są zdefiniowane wstdlib.h


20
0 jest również standardem.
Chris Young,

2
@ChrisYoung Jest EXIT_SUCCESSi EXIT_FAILUREdlatego, że niektóre historyczne systemy operacyjne (VMS?) Używały innej liczby niż 0 dla oznaczenia sukcesu. Obecnie wszędzie jest 0.
fuz

5
@FUZxxl masz rację, ale nie jest to sprzeczne z moim komentarzem. EXIT_SUCCESS może rzeczywiście być niezerowy, ale wszystkie standardy (C89, C99, C11) definiują 0 (podobnie jak EXIT_SUCCESS), aby być także zdefiniowaną implementacją formą pomyślnego zakończenia statusu.
Chris Young,

2
@FUZxxl: To prawda, że ​​VMS użył wartości nieparzystych (jak 1) do wskazania sukcesu, a nawet wartości (jak 0) do wskazania niepowodzenia. Niestety, oryginalny standard ANSI C został zinterpretowany w ten sposób, że EXIT_SUCCESS musiał wynosić 0, więc zwrócenie EXIT_SUCCESS z głównego spowodowało dokładnie nieprawidłowe zachowanie w VMS. Przenośną rzeczą do zrobienia dla VMS było użycie exit(EXIT_SUCCESS), co zawsze działało poprawnie.
Adrian McCarthy

1
5.1.2.2.3 "Jeśli typ zwracany przez funkcję główną jest typem zgodnym z int, powrót z początkowego wywołania do funkcji głównej jest równoważny z wywołaniem funkcji wyjścia z wartością zwróconą przez funkcję główną jako argument; 11) dotarcie do} kończącego główną funkcję zwraca wartość 0. ”
Lundin,

38

Zauważ, że standardy C i C ++ definiują dwa rodzaje implementacji: wolnostojące i hostowane.

  • Środowisko hostowane w C90

    Dozwolone formularze 1 :

    int main (void)
    int main (int argc, char *argv[])
    
    main (void)
    main (int argc, char *argv[])
    /*... etc, similar forms with implicit int */

    Komentarze:

    Pierwsze dwa są wyraźnie określone jako dozwolone formy, pozostałe są domyślnie dozwolone, ponieważ C90 dopuszcza „niejawne int” dla typu zwracanego parametru i parametrów funkcji. Żadna inna forma nie jest dozwolona.

  • Wolnostojące środowisko C90

    Dowolna forma lub nazwa magistrali jest dozwolona 2 .

  • Środowisko hostowane w C99

    Dozwolone formularze 3 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */

    Komentarze:

    C99 usunął „niejawne int”, więc main()nie jest już ważny.

    Wprowadzono dziwne, dwuznaczne zdanie „lub w inny sposób określony przez implementację”. Można to interpretować jako „parametry, które int main()mogą się różnić” lub jako „main może mieć dowolną formę zdefiniowaną w implementacji”.

    Niektóre kompilatory wybrały interpretację standardu w ten drugi sposób. Prawdopodobnie nie można łatwo stwierdzić, że nie są one ściśle zgodne z normą, powołując się na samą normę, ponieważ jest ona niejednoznaczna.

    Jednak dopuszczenie całkowicie dzikich form main()prawdopodobnie nie było (?) Intencją tego nowego zdania. Uzasadnienie C99 (nienormatywne) oznacza, że ​​zdanie odnosi się do dodatkowych parametrów do int main 4 .

    Jednak sekcja dotycząca zakończenia programu środowiska hostowanego dalej spiera się o przypadek, w którym main nie zwraca int 5 . Chociaż sekcja ta nie jest normatywna w zakresie sposobu deklaracji main, zdecydowanie oznacza, że ​​main może być zadeklarowany w sposób całkowicie zdefiniowany w implementacji, nawet na systemach hostowanych.

  • C99 wolnostojące środowisko

    Dowolna forma lub nazwa magistrali jest dozwolona 6 .

  • Środowisko hostowane w C11

    Dozwolone formularze 7 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */
  • C11 wolnostojące środowisko

    Dowolna forma lub nazwa magistrali jest dozwolona 8 .


Należy pamiętać, że int main()nigdy nie był wymieniony jako poprawny formularz dla hostowanej implementacji języka C w żadnej z powyższych wersji. W C, w przeciwieństwie do C ++ ()i (void)mają różne znaczenia. Ten pierwszy jest przestarzałą funkcją, którą można usunąć z języka. Zobacz przyszłe kierunki języka C11:

6.11.6 Deklaratory funkcji

Użycie funkcji deklarujących funkcje z pustymi nawiasami (nie deklarujących typu parametrów prototypowych) jest przestarzałą funkcją.


  • Środowisko hostowane w C ++ 03

    Dozwolone formularze 9 :

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

    Komentarze:

    Zwróć uwagę na pusty nawias w pierwszej formie. C ++ i C różnią się w tym przypadku, ponieważ w C ++ oznacza to, że funkcja nie przyjmuje parametrów. Ale w C oznacza, że ​​może przyjąć dowolny parametr.

  • C ++ 03 środowisko wolnostojące

    Nazwa funkcji wywoływanej podczas uruchamiania jest zdefiniowana w implementacji. Jeśli jest nazwany main(), musi spełniać podane formularze 10 :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])
  • Środowisko hostowane w C ++ 11

    Dozwolone formularze 11 :

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

    Komentarze:

    Tekst standardu został zmieniony, ale ma to samo znaczenie.

  • Wolnostojące środowisko C ++ 11

    Nazwa funkcji wywoływanej podczas uruchamiania jest zdefiniowana w implementacji. Jeśli jest nazwany main(), musi spełniać podane formularze 12 :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])

Bibliografia

  1. ANSI X3.159-1989 2.1.2.2 Środowisko hostowane. „Uruchomienie programu”

    Funkcja wywoływana podczas uruchamiania programu nosi nazwę main. Implementacja deklaruje brak prototypu dla tej funkcji. Należy go zdefiniować za pomocą zwracanego typu int i bez parametrów:

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

    lub z dwoma parametrami (określanymi tutaj jako argc i argv, chociaż można stosować dowolne nazwy, ponieważ są one lokalne dla funkcji, w której zostały zadeklarowane):

    int main(int argc, char *argv[]) { /* ... */ }
  2. ANSI X3.159-1989 2.1.2.1 Środowisko wolnostojące:

    W środowisku wolnostojącym (w którym wykonywanie programu C może odbywać się bez jakiejkolwiek korzyści systemu operacyjnego), nazwa i typ funkcji wywoływanej podczas uruchamiania programu są zdefiniowane pod względem implementacji.

  3. ISO 9899: 1999 5.1.2.2 Środowisko hostowane -> 5.1.2.2.1 Uruchomienie programu

    Funkcja wywoływana podczas uruchamiania programu nosi nazwę main. Implementacja deklaruje brak prototypu dla tej funkcji. Należy go zdefiniować za pomocą zwracanego typu int i bez parametrów:

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

    lub z dwoma parametrami (określanymi tutaj jako argc i argv, chociaż można stosować dowolne nazwy, ponieważ są one lokalne dla funkcji, w której zostały zadeklarowane):

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

    lub równoważny; 9) lub w inny określony sposób.

  4. Uzasadnienie międzynarodowego standardu - języki programowania - C, wersja 5.10. 5.1.2.2 Środowisko hostowane -> 5.1.2.2.1 Uruchomienie programu

    Zachowanie argumentów do main oraz interakcji exit, main i atexit (patrz §7.20.4.2) zostało skodyfikowane, aby ograniczyć pewną niechcianą różnorodność w reprezentacji ciągów argv oraz w znaczeniu wartości zwracanych przez main.

    Określenie argc i argv jako argumentów głównego uznaje rozległą wcześniejszą praktykę. Argv [argc] musi być wskaźnikiem zerowym, aby zapewnić zbędne sprawdzanie końca listy, również na podstawie powszechnej praktyki.

    main jest jedyną funkcją, która może być przenośnie zadeklarowana z zerowym lub dwoma argumentami. (Liczba argumentów innych funkcji musi dokładnie zgadzać się między wywołaniem a definicją.) Ten szczególny przypadek po prostu uznaje powszechną praktykę polegającą na pozostawianiu argumentów jako main, gdy program nie uzyskuje dostępu do ciągów argumentów programu. Podczas gdy wiele implementacji obsługuje więcej niż dwa argumenty do głównego, taka praktyka nie jest ani błogosławiona ani zabroniona przez Standard; program, który definiuje main z trzema argumentami, nie jest ściśle zgodny (patrz §J.5.1.).

  5. ISO 9899: 1999 5.1.2.2 Środowisko hostowane -> 5.1.2.2.3 Zakończenie programu

    Jeśli typ zwracany przez funkcję główną jest typem zgodnym z int, powrót z początkowego wywołania do funkcji głównej jest równoważny z wywołaniem funkcji wyjścia z wartością zwróconą przez funkcję główną jako argument; 11) osiągnięcie tego, }który kończy działanie funkcja główna zwraca wartość 0. Jeśli typ zwracany jest niezgodny z int, status zakończenia zwracany do środowiska hosta jest nieokreślony.

  6. ISO 9899: 1999 5.1.2.1 Środowisko wolnostojące

    W środowisku wolnostojącym (w którym wykonywanie programu C może odbywać się bez jakiejkolwiek korzyści systemu operacyjnego), nazwa i typ funkcji wywoływanej podczas uruchamiania programu są zdefiniowane pod względem implementacji.

  7. ISO 9899: 2011 5.1.2.2 Środowisko hostowane -> 5.1.2.2.1 Uruchomienie programu

    Ta sekcja jest identyczna z cytowaną powyżej C99.

  8. ISO 9899: 1999 5.1.2.1 Środowisko wolnostojące

    Ta sekcja jest identyczna z cytowaną powyżej C99.

  9. ISO 14882: 2003 3.6.1 Główna funkcja

    Implementacja nie powinna predefiniować głównej funkcji. Ta funkcja nie może być przeciążona. Powinien mieć typ zwracany typu int, ale w przeciwnym razie jego typ jest zdefiniowany w implementacji. Wszystkie implementacje powinny umożliwiać obie następujące definicje głównej:

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

    i

    int main(int argc, char* argv[]) { /* ... */ }
  10. ISO 14882: 2003 3.6.1 Główna funkcja

    Zdefiniowane jest wdrożenie, czy program w środowisku wolnostojącym jest wymagany do zdefiniowania głównej funkcji.

  11. ISO 14882: 2011 3.6.1 Główna funkcja

    Implementacja nie powinna predefiniować głównej funkcji. Ta funkcja nie może być przeciążona. Powinien mieć typ zwracany typu int, ale w przeciwnym razie jego typ jest zdefiniowany w implementacji. Wszystkie wdrożenia powinny umożliwiać jedno i drugie

    - funkcja () zwracająca int i

    - funkcja (int, wskaźnik do wskaźnika na char) zwraca int

    jako typ magistrali (8.3.5).

  12. ISO 14882: 2011 3.6.1 Główna funkcja

    Ta sekcja jest identyczna z C ++ 03 cytowaną powyżej.


Jedno pytanie: czy standardy C ++ oznaczają, że podpis funkcji startowej w środowiskach wolnostojących jest również zdefiniowany w implementacji? Na przykład implementacja mogła zdefiniować funkcję uruchamiania jako: int my_startup_function ()lub czy int my_startup_function (int argc, char *argv[])może ona mieć na przykład: char my_startup_function (long argc, int *argv[])jako funkcję uruchamiania? Chyba nie, prawda? Czy to również nie jest dwuznaczne?
Utku

@Utku Może mieć dowolny podpis, o ile nie jest nazwany, main()ponieważ wtedy musi użyć jednego z wymienionych podpisów. Wyobrażam sobie, że najbardziej powszechny byłby void my_startup_function (), ponieważ nie ma sensu wracać z programu na wolnostojące systemy.
Lundin,

1
Widzę. Ale jeśli dozwolone jest używanie dowolnej nazwy i dowolnego podpisu dla funkcji uruchamiania, dlaczego nie zezwolić również na użycie innego podpisu main? Przepraszam, jeśli to nie jest mądre pytanie, ale nie mogłem zrozumieć uzasadnienia.
Utku

@Utku C i C ++ są tam różne. Co do tego, dlaczego C ++ wymusza to, nie mam pojęcia, nie ma uzasadnienia. Podejrzewam, że głównym winowajcą (gra słów zamierzona) jest Stroustrup, który wcześnie zadeklarował, że main musi wrócić int, kropka. Ponieważ kiedy stworzył pierwszą wersję C ++, był przyzwyczajony tylko do systemów hostowanych. W łączonym poście Stroustrup nadal wydaje się nieświadomy istnienia niezależnych implementacji: na przykład ignoruje nawiązanie do hostowanego podrozdziału dotyczącego implementacji standardu C, ignorując istnienie rozdziału 5.1.2.1.
Lundin,

1
Godną uwagi cechą standardowego szkicu C11 jest to, że chociaż func()uważa się go za przestarzały, sam szkic używa int main()w swoich własnych przykładach.
Antti Haapala

29

Zwraca 0 w przypadku sukcesu i niezerową w przypadku błędu. Jest to standard używany przez skrypty UNIX i DOS, aby dowiedzieć się, co się stało z twoim programem.


8

main() w C89 i K&R C nieokreślone typy zwracane są domyślnie na „int”.

return 1? return 0?
  1. Jeśli nie napiszesz instrukcji return int main(), zamknięcie {zwróci domyślnie 0.

  2. return 0lub return 1zostanie odebrany przez proces nadrzędny. W powłoce przechodzi w zmienną powłoki, a jeśli program jest tworzony z powłoki i nie używasz tej zmiennej, nie musisz się martwić o wartość zwracaną main().

Zobacz Jak mogę uzyskać to, co zwróciła moja główna funkcja? .

$ ./a.out
$ echo $?

W ten sposób możesz zobaczyć, że to zmienna $?otrzymuje najmniej znaczący bajt wartości zwracanej main().

W skryptach Unix i DOS return 0zwykle zwracane są informacje o powodzeniu i niezerowej wartości błędu. Jest to standard używany przez skrypty Unix i DOS, aby dowiedzieć się, co się stało z twoim programem i kontrolować cały przepływ.


4
Ściśle mówiąc, $?nie jest zmienną środowiskową; jest to wstępnie zdefiniowana (lub wbudowana) zmienna powłoki. Różnica jest trudna do zauważenia, ale jeśli uruchomisz env(bez żadnych argumentów), wydrukuje środowisko i $?nie będzie widoczne w środowisku.
Jonathan Leffler

1
Automatyczne zwracanie 0, gdy główny „upadek końca” występuje tylko w C ++ i C99, a nie w C90.
Kaz

Literówka: {powinno być „zamykanie ” }. SO nie pozwoli mi więc na tak małą edycję.
Spencer

7

Pamiętaj, że nawet jeśli zwracasz liczbę całkowitą, niektóre systemy operacyjne (Windows) skracają zwracaną wartość do jednego bajtu (0–255).


4
Unix robi to samo, podobnie jak większość innych systemów operacyjnych. Wiem, że VMS robi z nim tak niesamowite rzeczy, że zwracanie czegokolwiek innego niż EXIT_SUCCESS lub EXIT_FAILURE wymaga problemów.
Leon Timmermans

2
MSDN zaczyna się różnić: po zgłoszeniu przez mscorlib kodem wyjścia jest 32-bitowa liczba całkowita ze znakiem . Wydaje się to sugerować, że biblioteki wykonawcze C, które obcinają kody wyjścia, są uszkodzone.

Tak, to nieprawda. W systemie Windows zwracana jest 32-bitowa liczba całkowita (i konwertowana na unsigned). To samo dotyczy systemów UNIX z 32-bitowymi liczbami całkowitymi. Ale powłoki w stylu UNIX w każdym systemie zwykle zachowują tylko 8-bitową liczbę całkowitą bez znaku.
John McFarlane

4

Wartość zwracana może być wykorzystana przez system operacyjny do sprawdzenia, jak program został zamknięty.

Zwracana wartość 0 zwykle oznacza OK w większości systemów operacyjnych (tych, o których i tak myślę).

Można to również sprawdzić podczas samodzielnego wywołania procesu i sprawdzić, czy program został poprawnie zakończony i zakończony.

To NIE jest tylko konwencja programowania.


W pytaniu nie ma nic, co wskazywałoby na obecność systemu operacyjnego. Zwracanie wartości nie ma sensu w systemie wolnostojącym.
Lundin,

3

Zwracana wartość main()pokazuje sposób wyjścia z programu. Jeśli zwracana jest wartość zero, oznacza to, że wykonanie zakończyło się powodzeniem, a każda niezerowa wartość będzie oznaczać, że coś poszło nie tak.


1
To jest komentarz, a nie odpowiedź na pytanie.
Lundin,

2

Miałem wrażenie, że standard określa, że ​​main nie potrzebuje wartości zwracanej, ponieważ pomyślny zwrot był oparty na systemie operacyjnym (zero w jednym może być sukcesem lub porażką w innym), dlatego brak zwrotu był wskazówką dla kompilator do wstawienia samego pomyślnego zwrotu.

Jednak zwykle zwracam 0.


C99 (i C ++ 98) pozwalają pominąć instrukcję return w main; C89 nie pozwala pominąć instrukcji return.
Jonathan Leffler

To jest komentarz, a nie odpowiedź.
Lundin,

To nie daje odpowiedzi na pytanie. Aby skrytykować lub poprosić autora o wyjaśnienia, zostaw komentarz pod postem.
Steve Lillis,

6
@ SteveLillis: W 2008 SO nie miała sekcji komentarzy.
graham.reeds

2

Zwrócenie 0 powinno poinformować programistę, że program pomyślnie zakończył zadanie.


Zwrócenie 1 z main()normalnie sygnalizuje wystąpienie błędu; zwracanie 0 oznacza sukces. Jeśli twoje programy zawsze zawodzą, to 1 jest w porządku, ale nie jest to najlepszy pomysł.
Jonathan Leffler

1
@JathanathanLeffler: Znaczenie powrotu 1z mainjest zdefiniowane w implementacji. Wartości tylko język zdefiniowane są 0, EXIT_SUCCESS(często definiowane jako 0), a EXIT_FAILURE. W OpenVMS return 1;oznacza pomyślne zakończenie.
Keith Thompson

VMS nie jest „normalny” - w rozumieniu tego, co powiedziałem. Czy nie jest to coś w rodzaju „jakakolwiek nieparzysta wartość to sukces; nawet wartości są błędami w VMS?
Jonathan Leffler

2

Pomijać return 0

Gdy program C lub C ++ osiągnie koniec mainkompilatora, automatycznie wygeneruje kod zwracający 0, więc nie ma potrzeby return 0;jawnego umieszczania go na końcu main.

Uwaga: kiedy piszę tę sugestię, prawie zawsze następuje jeden z dwóch rodzajów komentarzy: „Nie wiedziałem o tym”. lub „To zła rada!” Moje uzasadnienie jest takie, że bezpieczne i użyteczne jest poleganie na zachowaniu kompilatora wyraźnie obsługiwanym przez standard. Dla C, od C99; patrz ISO / IEC 9899: 1999 sekcja 5.1.2.2.3:

[...] powrót z początkowego wywołania mainfunkcji jest równoważny wywołaniu exitfunkcji z wartością zwróconą przez mainfunkcję jako argument; osiągnięcie tego, }który kończy mainfunkcję zwraca wartość 0.

Dla C ++ od pierwszego standardu w 1998 r .; patrz ISO / IEC 14882: 1998 sekcja 3.6.1:

Jeśli kontrola osiągnie koniec Main bez napotkania instrukcji return, efektem jest wykonanie return 0;

Wszystkie wersje obu standardów od tego czasu (C99 i C ++ 98) utrzymały ten sam pomysł. Opieramy się na automatycznie generowanych funkcjach członkowskich w C ++, a niewiele osób pisze wyraźne return;instrukcje na końcu voidfunkcji. Powody, dla których zaniechanie wydaje się sprowadzać do „wygląda dziwnie” . Jeśli, podobnie jak ja, jesteś ciekaw uzasadnienia zmiany standardu C, przeczytaj to pytanie . Zauważ też, że na początku lat 90. było to uważane za „niechlujną praktykę”, ponieważ było to wówczas zachowanie nieokreślone (choć powszechnie wspierane).

Ponadto Wytyczne podstawowe C ++ zawierają wiele instancji pomijania return 0;na końcu maini żadnych instancji, w których zapisany jest wyraźny zwrot. Chociaż w tym dokumencie nie ma jeszcze szczegółowych wytycznych na ten konkretny temat, wydaje się to przynajmniej milczącym poparciem dla tej praktyki.

Dlatego zalecam pominięcie tego; inni się nie zgadzają (często gwałtownie!) W każdym razie, jeśli napotkasz kod, który go pomija, będziesz wiedział, że jest on wyraźnie obsługiwany przez standard i będziesz wiedział, co to znaczy.


2
To zła rada, ponieważ kompilatory, które implementują tylko C89, a nie późniejsze standardy, są nadal niezwykle powszechne (piszę to w 2017 roku) i pozostaną niezwykle powszechne w najbliższej przyszłości. Na przykład ostatnio sprawdziłem, czy żadna wersja kompilatorów Microsoftu nie implementuje C99, i rozumiem, że nadal jest to typowe dla kompilatorów systemów wbudowanych, które nie są GCC.
zwolnienie

4
@zwol: Każdy, kto nie ma innego wyboru, jak tylko użyć kompilatora, który jest nieaktualny o 28 lat, prawdopodobnie ma więcej problemów niż decydowanie, czy wyraźnie dołączyć return 0;, jednak chciałbym zauważyć, że wiele kompilatorów z tej epoki również wdrożyło domniemane, return 0;jeszcze zanim było znormalizowany.
Edward

2
Właściwie pracuję wiele systemów wbudowanych i nie spotkałem się z kompilatorem, który nie obsługuje domyślnie return 0przez ponad dekadę. Również obecne wersje Microsoft C wspierać je jako dobrze . Być może Twoje informacje są nieaktualne?
Edward

2
Rozumiem, że jest to kontrowersyjne w C, prawie (per @ wyzwolenie). W C ++ wszelkie kontrowersje wokół tego są czystym nonsensem.
Wyścigi lekkości na orbicie

2
@Edward Nie powiedziałem, że kontrowersja nie istnieje, powiedziałem, że to nonsens: P
Wyścigi lekkości na orbicie

1

Co należy zwrócić, zależy od tego, co chcesz zrobić z plikiem wykonywalnym. Na przykład, jeśli używasz swojego programu z powłoką wiersza poleceń, musisz zwrócić 0 dla sukcesu i niezerowe dla niepowodzenia. Wtedy będziesz mógł używać programu w powłokach z przetwarzaniem warunkowym w zależności od wyniku twojego kodu. Można również przypisać dowolną wartość niezerową zgodnie z interpretacją, na przykład w przypadku błędów krytycznych różne punkty wyjścia programu mogą zakończyć program z różnymi wartościami wyjścia i który jest dostępny dla powłoki wywołującej, która może zdecydować, co zrobić, sprawdzając zwróconą wartość. Jeśli kod nie jest przeznaczony do używania z powłokami, a zwrócona wartość nikomu nie przeszkadza, można go pominąć. Ja osobiście używam podpisuint main (void) { .. return 0; .. }


Format main () jest określony przez implementację, co oznacza kompilator. Programista nie wybiera formy do wyboru, chyba że kompilator obsługuje kilka form.
Lundin,

@Lundin Typem zwrotu będzie implementacja przez implementację. Ale o wartości, która ma zostać zwrócona, decyduje programista. C99 Sekcja 5.1.2.2.3 wspomina, że ​​typ zwracanego towaru mainjest zgodny int. Dlatego powrót intnie będzie problemem. Chociaż dozwolone są inne typy zwracane, ale w takim przypadku zmienna środowiskowa o wartości zwracanej nie będzie określona. Ale jeśli programista to zrobi, return 0;wtedy w bashu można go użyć do tworzenia gałęzi.
phoxis,

1

Jeśli naprawdę masz problemy związane z wydajnością zwracania liczby całkowitej z procesu, prawdopodobnie powinieneś unikać wywoływania tego procesu tyle razy, że ta zwracana wartość staje się problemem.

Jeśli to robisz (wywołujesz proces wiele razy), powinieneś znaleźć sposób na umieszczenie logiki bezpośrednio w dzwoniącym lub w pliku DLL, bez przydzielania określonego procesu dla każdego połączenia; przydziały wielu procesów przynoszą w tym przypadku odpowiedni problem z wydajnością.

Szczegółowo, jeśli chcesz tylko wiedzieć, czy zwracanie 0 jest mniej lub bardziej wydajne niż zwracanie 1, w niektórych przypadkach może zależeć od kompilatora, ale ogólnie, zakładając, że są one odczytywane z tego samego źródła (lokalnego, pola, stałego, osadzonego w kodzie, wyniku funkcji itp.) wymaga dokładnie takiej samej liczby cykli zegara.


1

Oto mała demonstracja użycia kodów powrotu ...

Korzystając z różnych narzędzi udostępnianych przez terminal Linuksa, można użyć kodu powrotu na przykład do obsługi błędów po zakończeniu procesu. Wyobraź sobie, że obecny jest następujący plik tekstowy mój plik:

To jest przykład, aby sprawdzić, jak działa grep.

Po wykonaniu polecenia grep tworzony jest proces. Po przejściu (i nie zepsuciu) zwraca kod między 0 a 255. Na przykład:

$ grep order myfile

Jeśli zrobisz

$ echo $?
$ 0

dostaniesz 0. Dlaczego? Ponieważ grep znalazł dopasowanie i zwrócił kod wyjścia 0, co jest zwykle wartością kończącą się sukcesem. Sprawdźmy to jeszcze raz, ale z czymś, co nie znajduje się w naszym pliku tekstowym, a zatem nie zostanie znalezione dopasowanie:

$ grep foo myfile
$ echo $?
$ 1

Ponieważ grep nie dopasował tokena „foo” do zawartości naszego pliku, kod powrotu to 1 (jest to zwykły przypadek, gdy wystąpi awaria, ale jak wspomniano powyżej, masz wiele wartości do wyboru).

Teraz poniższy skrypt bash (wystarczy wpisać go w terminalu Linux), chociaż bardzo podstawowy powinien dać pojęcie o obsłudze błędów:

$ grep foo myfile
$ CHECK=$?
$ [ $CHECK -eq 0] && echo 'Match found'
$ [ $CHECK -ne 0] && echo 'No match was found'
$ No match was found

Po drugim wierszu nic nie jest drukowane na terminalu, ponieważ „foo” powoduje, że grep zwraca 1, a my sprawdzamy, czy kod powrotu grep był równy 0. Druga instrukcja warunkowa powtarza swój komunikat w ostatnim wierszu, ponieważ jest to prawdą z powodu SPRAWDŹ == 1.

Jak widać, jeśli wywołujesz ten i ten proces, czasami niezbędne jest sprawdzenie, co zostało zwrócone (według wartości zwracanej przez main ()).


Używałbyś w skrypcie powłoki if grep foo myfile; then echo 'Match found'; else echo 'No match was found'; fi- bezpośrednio testując status zwrotu. Jeśli chcesz uchwycić status (do raportowania itp.), Skorzystaj z zadania. Możesz użyć if grep foo myfile; CHECK=$?; [ "$CHECK" = 0 ]; then echo 'Match found'; else echo 'No match was found'; filub możesz użyć trzech linii. Możesz także użyć opcji -si, -qaby grepzapobiec wyświetlaniu dopasowań lub rutynowych komunikatów o błędach. Jest to jednak drobiazg powłoki - kluczowym punktem, w którym status wyjścia może być przydatny - jest OK.
Jonathan Leffler

1

Jaki jest prawidłowy (najbardziej wydajny) sposób zdefiniowania funkcji main () w C i C ++ - int main () lub void main () - i dlaczego?

Te słowa „(najbardziej wydajny)” nie zmieniają pytania. O ile nie znajdujesz się w wolnostojącym środowisku, istnieje jeden ogólnie poprawny sposób deklarowania main(), i to jako zwrot int.

Co powinien main() zwrócić w C i C ++?

To nie to, co powinien main() wrócić, to co robi main() zwrot. main()jest oczywiście funkcją, którą wywołuje ktoś inny. Nie masz żadnej kontroli nad wywoływanym kodemmain() . Dlatego musisz zadeklarować main()z poprawnym typem podpisu, aby pasował do dzwoniącego. Po prostu nie masz wyboru w tej sprawie. Nie musisz zadawać sobie pytania, co jest bardziej lub mniej wydajne, czy jest lepszy, czy gorszy styl, czy coś w tym rodzaju, ponieważ odpowiedź jest już dla Ciebie doskonale zdefiniowana przez standardy C i C +. Po prostu podążaj za nimi.

Jeśli int main (), to zwróć 1 lub zwróć 0?

0 dla sukcesu, niezerowe dla niepowodzenia. Ponownie, nie jest to coś, co musisz (lub trzeba) wybrać: jest to zdefiniowane przez interfejs, z którym powinieneś być zgodny.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.