C jest jednym z najczęściej używanych języków na świecie. Stanowi ogromną część istniejącego kodu i jest nadal używany do ogromnej ilości nowego kodu. Jest uwielbiany przez użytkowników, jest tak szeroko rozpowszechniony, że możliwość uruchamiania C jest dla wielu nieformalną definicją platformy i jest chwalony przez fanów za to, że jest „małym” językiem ze stosunkowo czystym zestawem funkcji.
Gdzie są więc wszystkie kompilatory?
Na pulpicie są (realistycznie) dwa : GCC i Clang. Myśląc o tym przez kilka sekund, zapewne pamiętasz, że Intel również istnieje. Istnieje kilka innych, zbyt niejasnych, aby przeciętny człowiek mógł je nazwać i prawie na ogół nie zawracają sobie głowy obsługą najnowszej wersji językowej (lub często nawet dobrze zdefiniowanego podzbioru językowego, tylko „podzbiór”). Połowa członków tej listy to przypisy historyczne; większość pozostałych jest bardzo wyspecjalizowanych i wciąż nie implementuje pełnego języka. Bardzo niewiele z nich wydaje się być oprogramowaniem typu open source.
Scheme i Forth - inne małe języki, które uwielbiają ich fani - prawdopodobnie mają więcej kompilatorów niż faktyczni użytkownicy. Nawet coś takiego jak SML ma bardziej „poważne” implementacje do wyboru niż C. Podczas gdy zapowiedź nowego (niedokończonego) kompilatora C mającego na celu weryfikację faktycznie dostrzega dość negatywne reakcje, a weterani implementacji walczą o to, aby uzyskać wystarczającą liczbę współpracowników, aby nawet nadrobić zaległości C99
Dlaczego? Czy wdrożenie C jest takie trudne? To nie jest C ++. Czy użytkownicy mają po prostu bardzo wypaczone wyobrażenie o tym, w jakiej grupie złożoności się znajduje (tzn. Że w rzeczywistości jest bliżej C ++ niż schematu)?
Odpowiedzi:
Dzisiaj trzeba prawdziwego kompilatora C być kompilator optymalizujący , zwłaszcza dlatego, że C nie jest językiem blisko sprzętu, ponieważ obecne procesory są niezwykle złożone ( out-of-order , potokowych , Superskalarna , ze złożonymi skrytek i TLB , dlatego wymaga planowania instrukcji itp.). Dzisiejsze procesory x86 nie przypominają procesorów i386 z poprzedniego wieku, nawet jeśli oba są w stanie uruchomić ten sam kod maszynowy. Zobacz, że C nie jest językiem niskiego poziomu (Twój komputer nie jest szybkim PDP-11), autorstwa Davida Chisnalla.
Niewiele osób używa naiwnych, nieoptymalizujących kompilatorów C, takich jak tinycc lub nwcc , ponieważ produkują kod, który jest kilka razy wolniejszy niż to, co mogą dać kompilatory optymalizujące.
Kodowanie optymalizującego kompilatora jest trudne. Zauważ, że zarówno GCC, jak i Clang optymalizują pewną reprezentację kodu „neutralną dla języka źródłowego” (Gimple dla GCC, LLVM dla Clang). Złożoność dobrego kompilatora C nie znajduje się w fazie analizy!
W szczególności tworzenie kompilatora C ++ nie jest dużo trudniejsze niż tworzenie kompilatora C: parsowanie C ++ i przekształcanie go w wewnętrzną reprezentację kodu jest złożone (ponieważ specyfikacja C ++ jest złożona), ale jest dobrze zrozumiana, ale części optymalizacyjne są jeszcze bardziej złożone (wewnątrz GCC: optymalizacje klasy środkowej, język neutralny i procesor docelowy są neutralne, stanowią większość kompilatora, a reszta jest zrównoważona między frontonami dla kilku języków i backendami dla kilku procesorów). Dlatego większość optymalizujących kompilatorów C jest w stanie kompilować niektóre inne języki, takie jak C ++, Fortran, D, ... Specyficzne części GCC dla C ++ stanowią około 20% kompilatora ...
Ponadto C (lub C ++) jest tak szeroko stosowane, że ludzie oczekują, że ich kod będzie podlegał kompilacji, nawet jeśli nie jest on zgodny z oficjalnymi standardami, które nie określają wystarczająco dokładnie semantyki języka (więc każdy kompilator może mieć własną interpretację tego). Spójrz także na sprawdzony kompilator CompCert C i analizator statyczny Frama-C , które dbają o bardziej formalną semantykę C.
Optymalizacje to zjawisko długofalowe : wdrożenie kilku prostych optymalizacji jest łatwe, ale nie uczynią kompilatora konkurencyjnym! Musisz wdrożyć wiele różnych optymalizacji oraz sprytnie je zorganizować i połączyć, aby uzyskać konkurencyjny kompilator w świecie rzeczywistym. Innymi słowy, kompilator optymalizujący w świecie rzeczywistym musi być złożonym oprogramowaniem. BTW, zarówno GCC, jak i Clang / LLVM, mają kilka wewnętrznych specjalistycznych generatorów kodu C / C ++. Oba są ogromnymi bestiami (kilka milionów linii kodu źródłowego, z roczną stopą wzrostu o kilka procent rocznie) z dużą społecznością programistów (kilkaset osób, pracujących głównie w pełnym wymiarze godzin lub przynajmniej w niepełnym wymiarze godzin).
Zauważ, że nie ma (według mojej najlepszej wiedzy) wielowątkowego kompilatora C, nawet jeśli niektóre części kompilatora mogłyby być uruchomione równolegle (np. Optymalizacja między procedurami, alokacja rejestru, planowanie instrukcji ...). A kompilacja równoległa z
make -j
nie zawsze wystarcza (zwłaszcza z LTO ).Ponadto trudno jest uzyskać fundusze na kodowanie kompilatora C od zera, a taki wysiłek musi trwać kilka lat. Wreszcie, większość kompilatorów C lub C ++ jest obecnie wolnym oprogramowaniem (nie ma już rynku na nowe kompilatory zastrzeżone sprzedawane przez start-upy) lub przynajmniej są to monopolistyczne towary (takie jak Microsoft Visual C ++ ), a bycie wolnym oprogramowaniem jest prawie wymagane dla kompilatorów ( ponieważ potrzebują wkładu wielu różnych organizacji).
Byłbym zachwycony, gdybym otrzymał fundusze na pracę nad kompilatorem C od zera jako wolne oprogramowanie, ale nie jestem wystarczająco naiwny, aby wierzyć, że jest to możliwe dzisiaj!
źródło
(there is no more a market for proprietary compilers
Powiedz to zespołowi Visual Studio ...Chciałbym zakwestionować twoje podstawowe założenie, że istnieje tylko niewielka liczba implementacji C.
Nawet nie znam C, nie używam C, nie jestem członkiem społeczności C, a jednak nawet wiem o wiele więcej niż kilka kompilatorów, o których wspomniałeś.
Przede wszystkim jest kompilator, który prawdopodobnie całkowicie przyćmiewa zarówno GCC, jak i Clanga na komputerze: Microsoft Visual C. Pomimo napadów, które zarówno OSX, jak i Linux robią na komputerze, i udział w rynku, że „ukradli” iOS i Android z dala od dawnych tradycyjnych komputerów stacjonarnych, Windows jest wciąż dominującym pulpit OS, a większość programów Windows Desktop C są prawdopodobnie skompilowany przy użyciu narzędzi Microsoft.
Tradycyjnie każdy sprzedawca systemu operacyjnego i każdy sprzedawca układów miał swoje własne kompilatory. Microsoft, jako dostawca systemu operacyjnego, ma Microsoft Visual C. IBM, zarówno jako dostawca systemu operacyjnego, jak i dostawca chipów, ma XLC (który jest domyślnym kompilatorem systemowym dla systemu AIX i kompilatorem, z którym kompilowane są zarówno systemy AIX, jak i i / OS) . Intel ma własny kompilator. Sun / Oracle ma własny kompilator w Sun Studio.
Są też wydajni dostawcy kompilatorów, tacy jak PathScale i The Portland Group, których kompilatory (i biblioteki OpenMP) są używane do łamania liczb.
Digital Mars nadal działa. Uważam, że Walter Bright wyróżnia się tym, że jest jedyną osobą na świecie, która samodzielnie stworzyła kompilator C ++ o jakości produkcyjnej (głównie).
Wreszcie, mamy wszystkie zastrzeżone kompilatory dla wbudowanych mikrokontrolerów. IIRC, każdego roku sprzedaje się więcej mikrokontrolerów niż komputery stacjonarne, urządzenia mobilne, serwery, stacje robocze i komputery mainframe sprzedawane w całej historii komputerów razem wziętych. To zdecydowanie nie są produkty niszowe.
Wyróżnienie otrzymuje TruffleC , interpreter C (!) Działający na JVM (!), Napisany przy użyciu frameworku interpretera AST Truffle, który jest tylko 7% wolniejszy niż GCC i Clang (w zależności od tego, który z nich jest najszybszy na danym poziomie) Gra Benchmark w językach komputerowych i szybsza niż oba na mikrodrukach. Korzystając z TruffleC, zespół Truffle był w stanie uzyskać swoją wersję JRuby + Truffle, aby wykonywać rozszerzenia Ruby C szybciej niż faktyczna implementacja C Ruby!
Jest to więc 6 implementacji oprócz wymienionych przez ciebie, które mogę wymienić z góry głowy, nawet nie wiedząc nic o C.
źródło
Ile kompilatorów potrzebujesz?
Jeśli mają różne zestawy funkcji, możesz stworzyć problem z przenośnością. Jeśli są towarem, wybierz opcję „domyślną” (GCC, Clang lub VS). Jeśli zależy Ci na ostatnich 5% wydajności, masz benchmark.
Jeśli programujesz język programowania rekreacyjnie lub do celów badawczych, prawdopodobnie będzie to język bardziej nowoczesny. Stąd rozprzestrzenianie się kompilatorów zabawek dla Scheme i ML. Chociaż wydaje się, że OCaml zyskuje przyczepność do nie-zabawkowych zastosowań nieakademickich.
Uwaga: różni się to bardzo w zależności od języka. Java ma w zasadzie łańcuch narzędzi Sun / Oracle i GNU. Python ma różne kompilatory, z których żaden nie jest naprawdę szanowany w porównaniu do standardowego interpretera. Rust i Go mają dokładnie jedną implementację. C # ma Microsoft i Mono.
źródło
1000 * 0
jest nadal0
.int
i będzie wymagał różnych kompilatorów do interpretacji tego samego kodu źródłowego na bardzo różne sposoby.6g
/8g
/… i gccgo). Istniała również bardzo interesująca, komercyjna implementacja o nazwie erGo, która była a) natywną implementacją Go dla Windows w czasach, gdy ani gccgo, ani oryginalny kompilator Go nie działały bardzo dobrze na Windows, b) firma obstawiająca Go, długo zanim stała się nawet 1.0, oraz c) pierwsza implementacja Go napisana w Go (gccgo i 6g / 8g są napisane w C). Zarówno projekt, jak i firma zniknęły, zanim jeszcze wyszły z zamkniętej wersji beta.C / C ++ jest unikalny wśród skompilowanych języków, ponieważ ma 3 główne implementacje wspólnej specyfikacji.
Zgodnie z zasadą odrzucania wszystkiego, co nie jest często używane, każdy inny skompilowany język ma wartość od 0 do 1.
I myślę, że javascript jest jedynym powodem, dla którego musisz określić „skompilowany”.
źródło
uint16_t a=48000u; unsigned uint32_t b=(a*a)/2;
jako przypisującyb
wartość 8192. Niektórzy definiują go jako przypisujący 1152000000. Większość obecnie uważa go za zachowanie niezdefiniowane i prawdopodobnie zapisze 3299483648, ale nie obiecuje pod tym względem.2
lub2u
najwyraźniej./2u
? Przepełnienie niepodpisane jest zdefiniowane (jako modulo 2 ^ N dla zdefiniowanego w implementacji N), ale podział nie może nawet przepełnić.int
, ale których produkt nie pasowałby do tego typu. Wymuszenie tego wyniku na unsigned int prawdopodobnie zmieni interpretację wartości wynikowej, ale nie neguje niezdefiniowanego zachowania z poprzedniego obliczenia.Jaki jest twój język docelowy?
Kompilatory SML często celują w C lub coś w rodzaju LLVM (lub jak widać w twoim linku, JVM lub JavaScript).
Jeśli kompilujesz C, to nie dlatego, że idziesz do JVM. Idziesz na coś gorszego niż C. Znacznie gorzej. A potem kilka razy zduplikujesz to drobne piekło dla wszystkich docelowych platform.
I oczywiście C nie jest C ++, ale powiedziałbym, że jest bliższy C ++ niż Scheme. Ma swój własny podzbiór nieokreślonego zachowania szatańskiego (patrzę na ciebie rozmiar wbudowanych typów). A jeśli spieprzysz te drobiazgi (lub zrobisz to „poprawnie”, ale nieoczekiwanie), masz dziesięciolecia istniejącego kodu w ważnych systemach, który powie ci, jak straszny jesteś. Jeśli spieprzysz kompilator SML, to po prostu nie zadziała - i ktoś może to zauważyć. Pewnego dnia.
źródło
int
się, że będzie to 32 lub 64 bity, ale może mieć nawet 16 bitów. Nie jest wcale trudno wyprodukować liczbę spoza zakresu,[−32767, +32767]
aint
przepełnienie to UB. Istnieje równieżchar
/short
awans doint
lub wunsigned int
zależności od tego, czyint
może reprezentować każdą wartość oryginalnego typu, co może dodatkowo wyzwalać konwersję zint
do,unsigned int
jeśli operandy miały różne typy i zostały przekonwertowane inaczej, a także potencjalnie kolejną konwersję, gdy przypiszesz wynik do zmiennej .