Pytam, ponieważ wydaje się, że mój kompilator tak myśli, chociaż ja nie.
echo 'int main;' | cc -x c - -Wall
echo 'int main;' | c++ -x c++ - -Wall
Clang nie wyświetla żadnego ostrzeżenia ani błędu, a gcc wydaje tylko łagodne ostrzeżenie:, 'main' is usually a function [-Wmain]
ale tylko wtedy, gdy jest skompilowane jako C. Podanie a -std=
nie wydaje się mieć znaczenia.
W przeciwnym razie kompiluje się i łączy poprawnie. Ale po wykonaniu kończy się natychmiast z SIGBUS
(dla mnie).
Czytanie (doskonałych) odpowiedzi w Co powinno zwracać main () w C i C ++? i szybkie przeglądanie specyfikacji języka, z pewnością wydaje mi się, że wymagana jest główna funkcja . Ale słownictwo z gcc -Wmain
(„main” jest zwykle funkcją) (i brak błędów tutaj) wydaje się sugerować coś innego.
Ale dlaczego? Czy istnieje jakieś dziwne, skrajne lub „historyczne” zastosowanie tego? Czy ktoś wie, co daje?
Chodzi mi o to, jak sądzę, że naprawdę uważam, że to powinien być błąd w środowisku hostowanym, co?
gcc -std=c99 -pedantic ...
-pedantic
lub bez-std
. Mój systemc99
również kompiluje to bez ostrzeżenia i błędu ...main
, co jest mało prawdopodobne, aby zadziałało. Jeśli zainicjujesz main z „właściwą” wartością, może ona faktycznie zwrócić ...main
)main=195;
Odpowiedzi:
Ponieważ pytanie jest podwójnie oznaczone jako C i C ++, rozumowanie C ++ i C byłoby inne:
xyz
i wolnostojącą funkcją globalnąxyz(int)
. Jednak nazwamain
nigdy nie jest zniekształcona.Oto, co się tutaj dzieje: linker spodziewa się znaleźć symbol
main
i tak się dzieje. „Przewija” ten symbol tak, jakby to była funkcja, bo lepiej nie zna. Część biblioteki uruchomieniowej, która przekazuje kontrolę, abymain
poprosić o nią konsolidatormain
, więc konsolidator nadaje jej symbolmain
, pozwalając na zakończenie fazy łączenia. Oczywiście to nie działa w czasie wykonywania, ponieważmain
nie jest to funkcja.Oto kolejna ilustracja tego samego problemu:
plik xc:
plik yc:
kompilacja:
To kompiluje się i prawdopodobnie by działało, ale jest to niezdefiniowane zachowanie, ponieważ typ symbolu obiecanego kompilatorowi różni się od rzeczywistego symbolu dostarczonego do konsolidatora.
Jeśli chodzi o ostrzeżenie, myślę, że jest rozsądne: C pozwala ci budować biblioteki, które nie mają
main
funkcji, więc kompilator zwalnia nazwęmain
do innych zastosowań, jeśli musisz zdefiniować zmiennąmain
z nieznanego powodu.źródło
main
nie podlega zniekształceniu nazw (tak się wydaje) w C ++, niezależnie od tego, czy jest to funkcja.main
coś innego niż funkcja. Odpowiedź zawiera wyjaśnienie obu części.main
Nie jest to słowo zarezerwowane jest to tylko predefiniowany identyfikator (jakcin
,endl
,npos
...), więc można zadeklarować zmiennąmain
, zainicjować go, a następnie wydrukować swoją wartość.Oczywiście:
main()
funkcji (bibliotek).EDYTOWAĆ
Niektóre odniesienia:
main
nie jest słowem zastrzeżonym (C ++ 11):C ++ 11 - [basic.start.main] 3.6.1.3
Słowa zastrzeżone w językach programowania .
Zarezerwowane słowa mogą nie zostać przedefiniowane przez programistę, ale predefiniowane często mogą zostać w pewnym stopniu nadpisane. Tak jest w przypadku
main
: istnieją zakresy, w których deklaracja używająca tego identyfikatora na nowo definiuje jego znaczenie.źródło
main()
funkcja, ale nie można połączyć go jako program. To, co się tutaj dzieje, to fakt, że „ważny” program jest łączony bezmain()
, tylkomain
.cin
iendl
nie znajdują się w domyślnej przestrzeni nazw - znajdują się wstd
przestrzeni nazw.npos
jest członkiemstd::basic_string
.main
jest zarezerwowana jako nazwa globalna. Żadna z innych rzeczy, o których wspomniałeś, ani niemain
są predefiniowane.main
może być. C ++ mówi „Implementacja nie powinna wstępnie definiować funkcji głównej”, a C mówi „Implementacja nie deklaruje żadnego prototypu dla tej funkcji”.Czy
int main;
jest prawidłowy program C / C ++?Nie jest do końca jasne, czym jest program C / C ++.
Czy
int main;
jest prawidłowy program w języku C?Tak. Dopuszcza się samodzielną realizację takiego programu.
main
nie musi mieć żadnego specjalnego znaczenia w wolnostojącym środowisku.To nie ważne w hostowane środowiska.
Czy jest
int main;
to poprawny program w C ++?Tak samo.
Dlaczego się psuje?
Program nie musi mieć sensu w Twoim środowisku. W środowisku wolnostojącym uruchamianie i kończenie programu oraz ich znaczenie
main
są zdefiniowane w ramach implementacji.Dlaczego kompilator mnie ostrzega?
Kompilator może ostrzec Cię o wszystkim, co mu się podoba, o ile nie odrzuci programów zgodnych z wymaganiami. Z drugiej strony ostrzeżenie to wszystko, co jest potrzebne do zdiagnozowania niezgodnego programu. Ponieważ ta jednostka tłumaczeniowa nie może być częścią prawidłowego programu hostowanego, uzasadniony jest komunikat diagnostyczny.
Czy jest
gcc
to środowisko wolnostojące, czy hostowane?Tak.
gcc
dokumentuje-ffreestanding
flagę kompilacji. Dodaj to, a ostrzeżenie zniknie. Możesz go użyć podczas budowania np. Jądra lub oprogramowania układowego.g++
nie dokumentuje takiej flagi. Wydaje się, że dostarczenie go nie ma wpływu na ten program. Prawdopodobnie można bezpiecznie założyć, że hostowane jest środowisko dostarczane przez g ++. Brak diagnostyki w tym przypadku jest błędem.źródło
Jest to ostrzeżenie, ponieważ nie jest to technicznie zabronione. Kod startowy użyje lokalizacji symbolu „main” i przeskoczy do niego z trzema standardowymi argumentami (argc, argv i envp). Tak nie jest, aw czasie łączenia nie może sprawdzić, czy jest to faktycznie funkcja, ani nawet czy ma te argumenty. Dlatego też działa int main (int argc, char ** argv) - kompilator nie wie o argumencie envp i po prostu nie jest używany, a jest to czyszczenie wywołania.
Jako żart możesz zrobić coś takiego
na maszynie x86 i ignorując ostrzeżenia i podobne rzeczy, będzie nie tylko kompilować, ale także działać.
Ktoś użył techniki podobnej do tej, aby napisać plik wykonywalny (rodzaj) działający bezpośrednio na wielu architekturach - http://phrack.org/issues/57/17.html#article . Został również użyty do wygrania IOCCC - http://www.ioccc.org/1984/mullender/mullender.c .
źródło
int main __attribute__ ((section (".text")))= 0xC3C3C3C3;
Czy to ważny program?
Nie.
Nie jest to program, ponieważ nie zawiera części wykonywalnych.
Czy kompilacja jest ważna?
Tak.
Czy można go używać z ważnym programem?
Tak.
Nie każdy skompilowany kod musi być wykonywalny, aby był prawidłowy. Przykładami są biblioteki statyczne i dynamiczne.
Skutecznie utworzyłeś plik obiektowy. Nie jest to poprawny plik wykonywalny, jednak inny program mógłby połączyć się z obiektem
main
w wynikowym pliku, ładując go w czasie wykonywania.Czy to powinien być błąd?
Tradycyjnie C ++ pozwala użytkownikowi robić rzeczy, które mogą wydawać się nieprzydatne, ale pasują do składni języka.
Chodzi mi o to, że na pewno można to przeklasyfikować jako błąd, ale dlaczego? Czemu miałoby to służyć, czemu nie służy ostrzeżenie?
Dopóki istnieje teoretyczna możliwość wykorzystania tej funkcji w rzeczywistym kodzie, jest bardzo mało prawdopodobne, aby wywołanie obiektu niebędącego funkcją
main
spowodowałoby błąd w zależności od języka.źródło
main
. W jaki sposób prawidłowy program, który musi mieć nazwaną zewnętrznie widoczną funkcjęmain
, może się z nią łączyć?main
funkcji.int main;
definicja nie będzie widoczna.Chciałbym uzupełnić odpowiedzi już udzielone, przytaczając aktualne standardy językowe.
Czy „int main”; ważny program w C?
Krótka odpowiedź (moja opinia): tylko jeśli Twoja implementacja korzysta z „wolnostojącego środowiska wykonawczego”.
Wszystkie poniższe cytaty z C11
5. Środowisko
5.1.2 Środowiska wykonawcze
5.1.2.1 Środowisko wolnostojące
5.1.2.2 Środowisko hostowane
5.1.2.2.1 Uruchomienie programu
Z nich obserwuje się, co następuje:
W wolnostojącym środowisku wykonawczym argumentowałbym, że jest to prawidłowy program, który nie pozwala na uruchomienie, ponieważ nie ma do tego żadnej funkcji, jak jest to wymagane w 5.1.2. W hostowanym środowisku wykonawczym, podczas gdy twój kod wprowadza obiekt o nazwie main , nie może dostarczyć wartości zwracanej, więc argumentowałbym, że nie jest to poprawny program w tym sensie, chociaż można również argumentować tak jak wcześniej, jeśli program nie jest przeznaczone do wykonania (np. on może chcieć podać dane tylko), to po prostu na to nie pozwala.
Czy „int main”; prawidłowy program w C ++?
Krótka odpowiedź (moja opinia): tylko jeśli Twoja implementacja korzysta z „wolnostojącego środowiska wykonawczego”.
Cytat z C ++ 14
3.6.1 Główna funkcja
Tutaj, w przeciwieństwie do standardu C11, mniej ograniczeń dotyczy niezależnego środowiska wykonawczego, ponieważ w ogóle nie wspomniano o funkcji startowej, podczas gdy w przypadku hostowanego środowiska wykonawczego sprawa jest prawie taka sama jak w przypadku C11.
Ponownie argumentowałbym, że dla przypadku hostowanego Twój kod nie jest prawidłowym programem w C ++ 14, ale jestem pewien, że jest to przypadek wolnostojący.
Ponieważ moja odpowiedź dotyczy tylko środowiska wykonawczego , myślę, że w grę wchodzi odpowiedź dasblinkenlicht, ponieważ zmiana nazwy występująca w środowisku tłumaczenia ma miejsce wcześniej. Tutaj nie jestem pewien, czy powyższe cytaty są tak ściśle przestrzegane.
źródło
Błąd jest twój. Nie określiłeś funkcji o nazwie,
main
która zwraca anint
i próbujesz użyć programu w środowisku hostowanym.Załóżmy, że masz jednostkę kompilacji, która definiuje zmienną globalną o nazwie
main
. Może to być zgodne z prawem w środowisku wolnostojącym, ponieważ to, co stanowi program, pozostaje do wdrożenia w środowiskach wolnostojących.Załóżmy, że masz inną jednostkę kompilacji, która definiuje funkcję globalną o nazwie,
main
która zwraca anint
i nie przyjmuje argumentów. Właśnie tego potrzebuje program w środowisku hostowanym.Wszystko jest w porządku, jeśli używasz tylko pierwszej jednostki kompilacji w środowisku wolnostojącym, a drugiej tylko w środowisku hostowanym. A jeśli użyjesz obu w jednym programie? W C ++ naruszyłeś regułę jednej definicji. To jest niezdefiniowane zachowanie. W C naruszyłeś zasadę, która mówi, że wszystkie odniesienia do pojedynczego symbolu muszą być spójne; jeśli nie są, jest to niezdefiniowane zachowanie. Niezdefiniowane zachowanie to „wyjdź z więzienia za darmo!” karta dla twórców implementacji. Wszystko, co implementacja robi w odpowiedzi na niezdefiniowane zachowanie, jest zgodne ze standardem. Implementacja nie musi ostrzegać, nie mówiąc już o wykrywaniu niezdefiniowanego zachowania.
Co się stanie, jeśli używasz tylko jednej z tych jednostek kompilacji, ale używasz niewłaściwej (co zrobiłeś)? W C sytuacja jest jasna. Brak zdefiniowania funkcji
main
w jednym z dwóch standardowych formularzy w środowisku hostowanym jest nieokreślonym zachowaniem. Przypuśćmy, że w ogóle nie zdefiniowałeśmain
. Kompilator / linker nie mówi nic o tym błędzie. To, że narzekają, jest dla nich przyjemnością. To, że program C skompilowany i zlinkowany bez błędów jest twoją winą, a nie kompilatorem.W C ++ jest to trochę mniej jasne, ponieważ brak zdefiniowania funkcji
main
w środowisku hostowanym jest raczej błędem niż niezdefiniowanym zachowaniem (innymi słowy, należy go zdiagnozować). Jednak reguła jednej definicji w C ++ oznacza, że konsolidatorzy mogą być raczej głupi. Zadaniem konsolidatora jest rozwiązywanie odwołań zewnętrznych, a dzięki zasadzie jednej definicji konsolidator nie musi wiedzieć, co oznaczają te symbole. Podałeś symbol o nazwiemain
, konsolidator oczekuje, że zobaczy symbol o nazwiemain
, więc wszystko jest w porządku, jeśli chodzi o konsolidator.źródło
Na razie dla C jest to zachowanie zdefiniowane w ramach implementacji.
Jak mówi ISO / IEC9899:
źródło
Nie, to nie jest prawidłowy program.
W przypadku C ++ zostało to ostatnio wyraźnie źle sformułowane przez raport defektu 1886: Linkowanie językowe dla funkcji main (), które mówi:
a część uchwały obejmowała następującą zmianę:
Możemy znaleźć to sformułowanie w najnowszym szkicu standardu C ++ N4527, który jest szkicem C ++ 1z.
Najnowsze wersje clang i gcc powodują teraz błąd ( zobacz to na żywo ):
Przed zgłoszeniem wady było to niezdefiniowane zachowanie, które nie wymaga diagnostyki. Z drugiej strony źle sformułowany kod wymaga diagnostyki, kompilator może ustawić to jako ostrzeżenie lub błąd.
źródło
main()
). Rozumiem uzasadnienie zakazumain()
posiadania wyraźnej specyfikacji powiązania, ale nie rozumiem, jak nakazuje tomain()
mieć powiązanie z C ++ . Oczywiście standard nie bezpośrednio adres jak radzić ABI powiązania / nazwa przekręcona, ale w praktyce (powiedzmy, ze Itanium ABI) spowodowałoby to magielmain()
do_Z4mainv
. czego mi brakuje?