Co powstrzymuje C przed kompilacją / interpretacją / JIT?

9

Java jest często chwalona za niesamowitą przenośność, która, jak przypuszczam, wynika z JVM. Moje pytanie brzmi: co powstrzymuje C przed kompilacją / interpretacją / JIT'em? Jeśli tak, C można również napisać raz i sprawić, by działał na dowolnym urządzeniu. ale nie jest to popularny mechanizm przetwarzania programu w języku C.

Jakie są wady przetwarzania C w ten sposób, a także jakie zalety ma przetwarzanie Java w ten sposób i brak kompilacji do kodu maszynowego, poza oczywiście przenośnością?

SphericalCow
źródło
Twoje pytanie ma już kilka bardzo dobrych odpowiedzi tutaj: stackoverflow.com/questions/3925947/…
Doc Brown
2
@delnan chodzi mi o to, że „to, co powstrzymuje C przed kompilacją / interpretacją / JIT'em” naprawdę traci znaczenie, gdy język może atakować maszynę wirtualną z JIT lub sytuacje, w których vm zidentyfikuje brakujące funkcje w sprzęcie i ponownie skompiluje kod pasujący do istniejącego sprzętu (takiego jak OpenGL (napisany w C) w OSX dla różnych kart graficznych). Nie, nie możesz pobrać czegoś skompilowanego do docelowego poziomu lvvm na jednym komputerze i uruchomić go na innym procesorze. Ale linia skompilowana / zinterpretowana / JIT może być dość rozmyta.
1
Java jest często atakowana ze względu na niesamowitą przenośność. Jest przenośny dla systemów, w których skompilowano JVM, to znaczy dla systemów, dla których skompilowano JVM (napisane w C). Nic nie stoi na przeszkodzie, aby obsługiwać kod C w ten sam sposób, z wyjątkiem tego, że nikt nie widzi wystarczającej korzyści z robienia tego, aby uzasadnić ten wysiłek.
Pete Becker
2
Zastanawiam się nad tym trochę: „co powstrzymuje kompilację C / [...]”. Nic?
Andres F.
1
Kompilatory C są obecnie dość szybkie, więc „make myprog.c; myprog” prawdopodobnie będzie działał szybciej niż większość interpreterów.
James Anderson

Odpowiedzi:

18

C to, co nazwałbym językiem średniego poziomu. Jego celem jest służyć jako „asembler bardzo wysokiego poziomu”, dlatego tak dobrze działa jako cel kompilatora i dlaczego tak dobrze obsługuje przenośność.

Historycznie tłumaczy zwykle używano z językami wysokiego poziomu, w kontekście wywołań metod. W najprostszej formie interpreter po prostu analizuje każde słowo kluczowe w języku źródłowym wraz z powiązanymi z nim tokenami i przekształca je w wywołania metod i parametry. W praktyce większość tłumaczy ustnych przekształca język źródłowy w jakąś pośrednią reprezentację i to właśnie interpretacja jest interpretowana.

Co powstrzymuje C przed interpretacją lub odrzuceniem? Nic. Ale to nie jest racja bytu C.

Robert Harvey
źródło
6

Przede wszystkim warto zauważyć, że JVM firmy Sun został napisany w C. C jest bardzo popularnym językiem, gdy wymagana jest przenośność.

C język jest przenośny choć wiele C programy nie są. Wynika to z faktu, że C nie nakłada na programistę tylu ograniczeń ani nie przyjmuje tylu założeń. Jeśli programista C chce, aby jego programy były przenośne, musi nałożyć te ograniczenia na siebie.

W praktyce nie jest to o wiele trudniejsze niż życie z ograniczeniami, które narzuca na ciebie Java. Chodzi przede wszystkim o to, aby pamiętać o swoim endianizmie i prymitywnych rozmiarach oraz używać przenośnych bibliotek takich jak GTK + zamiast bibliotek specyficznych dla platformy.

Możesz stworzyć obiekt docelowy GTK + i kompilator C obsługujący maszynę wirtualną, nawet prawdopodobnie JVM, i uzyskać istniejący kod do pracy z niewielkimi zmianami. W rzeczywistości bez wyrzucania elementów bezużytecznych maszyna wirtualna C byłaby prawdopodobnie znacznie prostsza. Dlaczego jednak chcesz?

Odwrotna kompilacja Javy do kodu natywnego jest również wykonalna. Tak właśnie działa JIT. Dlaczego jednak chcesz? Jestem pewien, że istnieją projekty dla zwierząt domowych „tylko dlatego, że”, ale nie są one poważnie wykorzystywane.

Karl Bielefeldt
źródło
5

Powiedziałeś:

Java jest często chwalona za niesamowitą przenośność, która, jak przypuszczam, wynika z JVM.

I tam, w pierwszym zdaniu, mylisz się. Java nie jest przenośna z powodu JVM. Java jest przenośna, ponieważ język Java jest zdefiniowany w sposób, który nie pozostawia implementatorowi żadnej swobody w zachowaniu się programu.

Na przykład Java ma dwa typy „int” (32-bitowa liczba całkowita ze znakiem) i „long” (64-bitowa liczba całkowita ze znakiem). C i C ++ mają „int” (podpisane co najmniej 16 bitów), „long” (podpisane co najmniej 32 bity) i „long long” (podpisane co najmniej 64 bity). To dlatego, że C ma działać na wielu różnych procesorach i pozwala im zachowywać się inaczej.

C mógł zdefiniować stałe rozmiary dla tych typów. Gdyby tak było, wówczas 36-bitowe procesory nie mogłyby zaimplementować języka C. I tak naprawdę nie mogą implementować Java! Tak więc C pozwolił, aby język działał na różnych komputerach. Jest nieuniknione, że pozwala to na tworzenie kodu, który nie jest przenośny. To kwestia języka.

gnasher729
źródło
Możliwe jest emulowanie 32-bitowej arytmetyki na maszynie 36-bitowej ORAZ wynik każdej operacji z 0xFFFFFFFF w celu obcięcia go do 32-bitów. Tak więc, te maszyny mogłyby implementować Javę, byłoby to po prostu wolniejsze niż gdyby Java dopuszczała typy nie oparte.
dan04
4

Java jest wysoce przenośna, ponieważ język jest przeznaczony na wirtualną maszynę Java, która, jak sama nazwa wskazuje, nie jest prawdziwą maszyną . Ponieważ można wdrożyć maszynę wirtualną na architekturze wielu różnych typów prawdziwych maszyn, program oparty na JVM jest wysoce przenośny.

Z drugiej strony C jest specjalnie zaprojektowany do działania na prawdziwym sprzęcie, ponieważ został stworzony w celu implementacji systemu operacyjnego, który wymaga pełnego dostępu do sprzętu. Oznacza to, że kod C nie jest szczególnie przenośny z założenia , a gdy przenosisz program C z jednej platformy na drugą, różne części, które są specyficzne dla architektury docelowej, będą musiały zostać przepisane do tego stopnia.

Mason Wheeler
źródło
7
C jest wysoce przenośny. Musisz tylko ponownie skompilować na platformie docelowej i unikać tych kilku bitów, które są specjalnie i celowo nieprzenośne.
Robert Harvey
5
@RobertHarvey: ... takie podstawowe rzeczy jak rozmiar różnych prymitywów? ;)
Mason Wheeler
2
Tak, te rzeczy. Niestety, problem istnieje, ale język jest w pełni przenośny pod każdym innym względem i istnieją sposoby, aby upewnić się, że pierwotne rozmiary działają na wszystkich platformach.
Robert Harvey
3
@RobertHarvey: Powiedziałbym, że C umożliwia pisanie programów przenośnych, ale z natury nie jest to łatwe.
Doc Brown,
2
@RobertHarvey: czy chcesz rozpocząć wojnę religijną? ;-) Moim ulubionym przenośnym językiem jest Python.
Doc Brown
3

W rzeczywistości istnieją interpretowane wersje C , ale w większości są przeznaczone do szybkiego eksperymentowania, a nie do systemu produkcyjnego.

Nie są one powszechne, ponieważ w końcu, dlaczego miałbyś cierpieć wszystkie dziwactwa C, gdyby nie uzyskać małego, szybkiego i statycznego pliku wykonywalnego?

fortran
źródło
3

Teoretycznie zarówno C, jak i Java mogą być kompilowane do natywnego kodu, interpretowane lub kompilowane na maszynie wirtualnej.

Technicznym powodem, dla którego C nie jest skompilowany na maszynie wirtualnej, jest po prostu brak standardowej wirtualnej maszyny C.

I wydaje się, że nikt nie chce definiować wirtualnej maszyny C, ani nawet kompilować do wirtualnej maszyny Java (co jest całkowicie możliwe). Prawdopodobnie dlatego, że nikt, kto używa C, nie chce stracić swojej niezrównanej prędkości. Prawdopodobnie również dlatego, że C jest najsilniejszy w społeczności open source, która może z łatwością wykonywać przenośność poprzez kompilację (dystrybuować i rekompilować źródło i wykonywać), więc nie odczuwa takiej potrzeby przenośności wykonania (dystrybuowania i wykonywania pliku binarnego) jako zamkniętego deweloper źródła.

cmaster - przywróć monikę
źródło
1

W rzeczywistości jest to zrobione. Istnieją główne kompilatory, które obsługują kompilację do LLVM (wiem, że clang to robi i myślę, że gcc również.). Ten LLVM może być JIT, podobnie jak kod Java jest kompilowany do kodu bajtowego, który jest JIT.

Jednak to, co sprawia, że ​​Java jest „wieloplatformowa” w porównaniu z C, to że Java ma dużą bibliotekę wykonawczą, która została przeniesiona na wiele platform. C wyraźnie nie przestrzega tego paradygmatu.

Cort Ammon
źródło
C z POSIX może być dość przenośny (do dowolnego systemu POSIX), jeśli kodujesz ostrożnie.
Basile Starynkevitch
0

Istnieją poważne różnice między Javą a C. Java jest izolowana od systemu operacyjnego za pośrednictwem wirtualnej maszyny Java (JVM). JVM oddziela system operacyjny od programu. Aplikacja Java może poprosić JVM o kawałek pamięci, a następnie JVM poprosi system operacyjny o tę pamięć. Istnieje wiele maszyn JVM dla różnych platform / systemów operacyjnych. JVM pozwala na uruchamianie tego samego programu Java na różnych platformach.

W przypadku C nie ma izolacji systemu operacyjnego. Programy C (zwykle) działają bezpośrednio na systemie operacyjnym, wykonując bezpośrednie wywołania systemu operacyjnego. To powoduje, że program C łączy się z określonym systemem operacyjnym / platformą. Każdy nietrywialny program będzie wykonywał połączenia z systemem operacyjnym. Ponadto programy C są kompilowane do kodu maszynowego, który jest specyficzny dla sprzętu. Skompilowanego programu C dla x86 nie można uruchomić bezpośrednio na procesorze ARM.

CurtisHx
źródło
1
Java jest kompilowana do kodu bajtowego niezależnego od platformy, który może (przynajmniej teoretycznie) zostać wykonany przez dowolną maszynę JVM na dowolnej platformie. C jest kompilowany do języka asemblera dla dowolnego procesora, na który celujesz (więc jeśli celujesz w architekturę x86, kompilator C utworzy asembler x86 lub asembler amd64, jeśli celujesz w tę architekturę lub asembler ARM itp.). Następnie język asemblera zamienia się w pliki obiektowe (asembler binarny, naprawdę), które są połączone w plik wykonywalny (kilka różnych formatów, w zależności od komputera docelowego).
Craig
1
W specyfikacji języka Java nie ma nic, co mówi o JVM, a tak naprawdę istnieją implementacje Java bez JVM. Na Androidzie programy Java działają na maszynie wirtualnej Dalvik (obecnie nieaktualne) lub Android Runtime, są implementacje Java dla CLI, implementacje kompilujące się do ECMAScript oraz implementacje kompilujące się do kodu natywnego. Istnieją kompilatory C kompilujące się do JVM. Istnieją kompilatory C kompilujące się do ECMAScript. Są tłumacze języka C.
Jörg W Mittag