Dlaczego `main` nie może zwrócić double lub String zamiast int lub void?

38

W wielu językach, takich jak C, C ++ i Java, mainmetoda / funkcja ma typ zwracany voidlub int, ale nie doublelub String. Jakie mogą być tego przyczyny?

Wiem trochę, że nie możemy tego zrobić, ponieważ mainjest wywoływana przez bibliotekę wykonawczą i oczekuje pewnej składni, jakoś tak int main()czy inaczej int main(int,char**), musimy się do tego trzymać.

Więc moje pytanie brzmi: dlaczego mainma podpis typu, który ma, a nie inny?

JAWA
źródło
15
Co podwójną wartość zwracana będzie oznaczać ? Co to jest wartość powrotu ciąg będzie oznaczać ?
1
ki rozumiem, że to nic nie znaczy. Ale jakieś inne powody i konwencje?
JAVA,
1
myślę, że to nic nie znaczy, po prostu dlatego, że ogólnie wybrano 0 dla normalnego wyjścia i niezerową dla nienormalnego. Int wybrano jako najprostszy typ danych z szeroką kompatybilnością międzyjęzykową. @ delnan
JAVA
@sunny Z tego, co udało mi się wyciągnąć z mojego doświadczenia z uniksowymi systemami operacyjnymi, 0 jest używane jako „normalne wyjście” (0 błędów), ponieważ jest jednoznaczne w porównaniu z innymi wartościami całkowitymi. Ponieważ większość (nie wszystkie) współczesne języki są zaprojektowane tak, aby były podobne (jeśli nie zostały zaprojektowane z tyłu) C, a ponieważ C był używany do pisania Uniksa, powiedziałbym, że była to historyczna decyzja KnR.
Jamie Taylor,
3
@sunny „szeroka kompatybilność między językami” nie stanowiła problemu. C i UNIX zostały napisane w tandemie. Powodem, dla którego wiele innych języków zwraca int, jest to, że zostały zaprojektowane do pracy w środowiskach UNIX lub podobnych do UNIX.

Odpowiedzi:

83

Zwracana wartość mainma być przekazywana do systemu operacyjnego ( dowolnego systemu operacyjnego) w jeden, spójny sposób. Informacje, które musi znać system operacyjny to „czy program zakończył się pomyślnie, czy wystąpił błąd?”

Jeśli jest to ciąg znaków, odpowiedź staje się trudna w różnych językach. Elementy wewnętrzne łańcucha Pascal (pierwszy bajt to długość) i łańcucha FORTRAN (ustalony, uzupełniony do pewnej wartości) i łańcuch C (zakończony zerem) są różne. Utrudnia to zwracanie spójnej wartości systemowi operacyjnemu. Zakładając, że problem został rozwiązany, co byś zrobił, aby odpowiedzieć na pytanie, jakie system operacyjny ma na temat programu? Porównywanie ciągów jest obarczone błędami („sukces” vs. „sukces”) i chociaż błąd może być bardziej użyteczny dla człowieka, trudniej jest poradzić sobie z systemem operacyjnym lub innym programem (powłoką). Istniały również znaczne różnice nawet w samych ciągach - EBCDIC (ze wszystkimi stronami kodowymi) w porównaniu z ASCII.

Liczba zmiennoprzecinkowa i liczba podwójna nie dostarczają żadnej dodatkowej wartości ponad liczbę całkowitą do przesyłania danych zwrotnych do systemu operacyjnego (i powłoki). W większości przypadków żadna z tych części komputera nie zajmuje się liczbami zmiennoprzecinkowymi. Nie można również wyliczyć liczby podwójnej, co utrudnia porównania. Nie można ich wyliczyć, dlatego zgłaszają, jaki był błąd (zakładając, że wybrałeś określoną wartość sukcesu). Ponownie, zmiennoprzecinkowe nie są spójne - zmiennoprzecinkowa na maszynie 8-bitowej była inna niż zmiennoprzecinkowa na maszynie 16-bitowej i 32-bitowej (i to tylko te „normalne” - nawet w IBM, zmiennoprzecinkowa nie była znormalizowana między maszynami tego samego producenta do lat osiemdziesiątych). A potem masz komputery dziesiętne kontra binarne. Wartości zmiennoprzecinkowe nie są spójne i nie dostarczają istotnych danych z powrotem.

To naprawdę pozostawia nam bajt i liczbę całkowitą jako opcje. Konwencja, która została ustanowiona jako „0”, była sukcesem, a wszystko inne było błędem. Liczba całkowita daje więcej miejsca niż bajt na zgłoszenie błędu. Można go wyliczyć (powrót 1 oznacza XYZ, powrót 2 oznacza ABC, powrót 3, oznacza DEF itp.) Lub można go użyć jako flagi ( 0x0001oznacza to, 0x0002że się nie powiedzie, oznacza, że ​​się nie powiedzie, 0x0003oznacza to to i to, co się nie powiedzie). Ograniczenie tego do bajtu może z łatwością zabraknąć flag (tylko 8), więc prawdopodobnie zdecydowano się na użycie liczby całkowitej.

Sean Allred
źródło
2
Myślę, że main jest wywoływany przez bibliotekę wykonawczą c / c ++ przed wywołaniem go przez os, który jest także kawałkiem kodu ładowanym wraz z naszym kodem i wywoływanym przez os @ MichaelT
JAVA
5
main()jest wywoływany na różne sposoby w różnych systemach operacyjnych. W C jak początkowo wywoływana jest metoda main ()? wchodzi w to.
22
Myślę, że kluczową kwestią do zrozumienia jest to, że main- w przeciwieństwie do innych funkcji w dowolnym programie - nie jest częścią protokołu zdefiniowanego przez programistę, ale protokół używany do komunikacji z hostem (OS). Nie możesz go wybrać, ponieważ nigdy nie był twój. Na bardziej pragmatycznym poziomie, UNIX oczekuje, że int zostanie zwrócony przez proces, więc protokół C-to-UNIX robi dokładnie to. Analogiczny argument można podać dla przekazywania argumentów: jeśli C został wynaleziony dla systemu operacyjnego / hosta, który przekazał tylko liczby jako argumenty (np. Bez wiersza poleceń), argumentami byłyby liczby całkowite zamiast ciągów.
Euro Micelli,
2
IBM przeniósł koncepcję stron kodowych z EBCDIC na swoje komputery. Nadal nas prześladują, trwając 35 lat po wprowadzeniu IBM 5150. 7-bitowy kod ASCII nie zawiera stron kodowych , ale 8-bitowe kody znaków można interpretować na wiele różnych sposobów, nawet na jednym komputerze, w zależności od ustawień - - nie mówiąc już o stronach kodowych kodujących kodowanie wielobajtowe. Jest to więc nawet gorsze niż to, o czym wspominasz w ostatnim zdaniu drugiego akapitu.
CVn
@EuroMicelli to bardzo miła informacja, właściwie dzięki za to :)
JAVA,
27

Cóż, może .

Na przykład w dialekcie C używanym w systemie operacyjnym Plan 9main jest zwykle deklarowany jako voidfunkcja, ale status wyjścia jest zwracany do środowiska wywołującego poprzez przekazanie wskaźnika exits()funkcji do funkcji. Pusty ciąg oznacza sukces, a każdy niepusty ciąg oznacza pewne niepowodzenie. To mógłby być realizowany poprzez mainzwraca char*wynik.

I z pewnością byłoby możliwe wdrożenie systemu ze statusem floatlub doublezakończeniem.

Więc dlaczego int? To tylko kwestia konwencji - a ogromną wartość ma to, że działające pod nimi systemy operacyjne i programy podlegają wspólnej konwencji.

Konwencja uniksowa polega na użyciu kodu statusu liczby całkowitej, przy czym zero oznacza sukces i niezerowy błąd oznaczający (ponieważ zazwyczaj jest tylko jeden sposób na sukces, ale wiele sposobów na porażkę). Nie wiem, czy ta konwencja pochodzi od Uniksa; Podejrzewam, że pochodzi z wcześniejszych systemów operacyjnych.

Zmienna zmiennoprzecinkowa byłaby trudniejszą konwencją, ponieważ (a) obsługa zmiennoprzecinkowa nie jest uniwersalna, (b) trudniej jest zdefiniować odwzorowanie między wartościami zmiennoprzecinkowymi a warunkami błędu, (c) różne systemy używają różnych zmiennoprzecinkowych reprezentacje punktów i (d) wyobraź sobie zabawę ze śledzenia błędu zaokrąglania w statusie wyjścia z programu. Z drugiej strony liczby całkowite bardzo dobrze nadają się do wyliczania kodów błędów.

Jak już wspomniałem, Plan 9 używa ciągów, ale narzuca to pewną złożoność zarządzania pamięcią, kodowania znaków itp. O ile wiem, był to nowy pomysł, kiedy Plan 9 go zaimplementował i nie zastąpił istniejącego powszechna konwencja.

(Nawiasem mówiąc, w C ++ mainmożna tylko powrócić int, a w C void mainjest dozwolone tylko wtedy, gdy kompilator specjalnie go obsługuje. Wiele kompilatorów nie narzeka zbyt głośno, jeśli piszesz void main, ale tylko niewielką przesadą jest stwierdzenie, że jest źle .)

Keith Thompson
źródło
9

Wartość zwracana przez metodę główną to „kod wyjścia”. Jest używany przez aplikację wywołującą (zwykle bash), aby sprawdzić, czy program zakończył się zgodnie z oczekiwaniami. Zwracanie liczby całkowitej jest najłatwiejszym sposobem na zrobienie tego na poziomie systemu operacyjnego. Double nie ma sensu dla kodu błędu, a String jest trudny do utrzymania na poziomie systemu operacyjnego (nie ma GC).

kseranik
źródło
3
Dlaczego ciąg należy zbierać śmieci, podczas gdy liczba całkowita nie?
Brad
4
@Brad, ciągi mają zmienną długość i byłyby w istocie tym samym, co przekazywanie tablicy, która może być jednym znakiem lub tysiącem. Pamięć dynamiczna byłaby uciążliwa, podczas gdy int jest raczej stałym rozmiarem, który nie jest tak trudny w obsłudze.
JB King