20/04/2023

Otkrijte tajne rada kompajlera C programskog jezika, saznajte kako vaš kod postaje izvršni

Kompajler je softverski alat koji se koristi za prevođenje izvornog koda napisanog na nekom programskom jeziku u izvršni kod koji može biti izvršen na računaru. Ovo je proces koji obuhvata nekoliko faza, uključujući leksičku analizu, sintaksnu analizu i semantičku analizu izvornog koda. Kompajler zatim generiše izvršni kôd koji je spreman za izvršavanje. Iako se može koristiti jednostavan jezik za objašnjavanje kompajlera, razumevanje njegovog rada i procesa prevođenja može biti od suštinske važnosti za softver inženjere i programere koji kodiraju softver. Zato smo ovu temu odvojili od prethodnog posta, pogledajte ovde; i dali mu poseban značaj. Prvo dobro pitanje koje bi ste verovatno imali za mene jeste, u kom programu je napisan kompajler za C programski jezik? Ukoliko se vratimo malo u istoriju, mi znamo da su stariji računari uglavnom koristili asemblerski jezik, dok su viši programski jezici počeli da se razvijaju kada su se prednosti ponovnog korišćenja softvera na različitim procesorima povećale. Prvi viši programski jezik, Plankalkül, je predložen još 1943. godine. Od tada, razvijeno je nekoliko eksperimentalnih kompajlera. Fortran-ov tim, predvođen John Backus -om iz IBM-a, predstavio je prvi kompletni kompajler 1957 godine. Od tada kompajleri su postajali sve složeniji kako su se računarske arhitekture razvijale. Danas je uobičajena praksa implementirati kompajlere u istom jeziku koji se kompajlira. Zato se pretpostavlja da je kompajler C programskog jezika, kodiran u C programskom jeziku, kao što na primer svi .Net programski jezici imaju Microsoft-ov kompajler otvorenog koda zvani Roslyn koji je napisan u C# programskom jeziku. Međutim, za stvaranje prvog C kompajlera, njegov tvorac Dennis Ritchie je koristio prethodni programski jezik B, koji je razvio Ken ThompsonDennis Ritchie je kasnije proširio B programski jezik i stvorio C programski jezik, pa je originalni C kompajler takođe pisan na B jeziku. Mi uglavnom koristimo GCC kompajler verziju 12 ili neku noviju, ovaj tekst je pisan 2023 godine; i nema teoretske šanse da ćete u njemu pronaći ni jednu komandu u B programskom jeziku. Zato što GCC kompajler ne podržava B programski jezik, smatra ga previše zastarelim. Ali znamo da je GCC kompajler napisan kombinacijom C i C++ programskog jezika, s time da je moguće da sadrži i neke delove napisane u drugim programskim jezicima kao što su Objective-C i neki noviji.

( Proces C kompajliranja od izvršnih datoteka do izvršnog fajla )

Kada bez greške napišete C kôd u bilo kojem tekstualnom editoru i stvorite tekstualni fajl, možete pozvati C kompajler da ga prevede u mašinski kôd, tako da vaš program može raditi. Kompajler pokreće "prevodilac" ili "jedinicu za prevođenje", poznatu kao "Translation Unit", koja se sastoji od izvorne datoteke i datoteka zaglavlja koje se navode pomoću direktiva #include. Ako je vaš kôd ispravan, prevodilac stvara Object File - objektnu datoteku, koju prepoznajemo po sufiksu .o ili .obj, a takve objektne datoteke nazivamo modulima. Standardna biblioteka programskog jezika C sadrži prevedene objektne datoteke na mašinski jezik, što omogućuje brži pristup standardnim funkcijama koje pozivamo u našim programima. Važno je napomenuti da kada kažemo da se datoteka prevodi na mašinski jezik kod C programskog jezika, prvo se prevodi na asemblerski programski jezik u privremenu datoteku, koja se zatim prevodi u mašinski jezik, nakon čega se privremena datoteka briše. Pri kompajliranju programa, takvu datoteku prepoznajemo po sufiksu .s. Prevodilac prevodi zasebno svaku izvornu datoteku sa svim datotekama zaglavlja koje sadrži u razdvojene objektne datoteke tj. module. Prevodilac onda poziva Linker – pokazivač, koji kombinuje sve objektne datoteke i sve korišćene funkcije iz biblioteke u Executable File - izvršnu datoteku. Nemojte ovaj proces mešati sa .Net tehnologijom. U .Net  tehnologiji i C# programskom jeziku, stvari su drugačije. 

Kroz koje sve faze prolazi proces prevođenja C programskog jezika?

Proces prevođenja se izvodi u 8 logičkih koraka, stime da prevodilac može da kombinuje nekoliko faza sve dok to ne utiče na rezultat.

1. Faza

Iz izvorne tekstualne datoteke čitaju se znakovi i tamo gde je potrebno se prevode u znakove iz skupa izvornih datoteka. Svi trigrafi se pretvaraju u znak koji predstavljaju. Trigrafi su troznakovne leksičke jedinice: 

  • ??(  - ekvivalent [
  • ??)  - ekvivalent ]
  • ??< - ekvivalent {
  • ??> - ekvivalent }
  • ??= - ekvivalent #
  • ??/ - ekvivalent \
  • ??! - ekvivalent |
  • ??’ - ekvivalent ^
  • ??- - ekvivalent ~

Digrafi se ne zamenjuju odgovarajućim znakom. Digrafi su dvoznakovne leksičke jedinice:

  •  <:        - ekvivalent [
  •  :>        - ekvivalent ]
  •  <%     - ekvivalent {
  •  %>     - ekvivalent }
  • %:      - ekvivalent #
  • %:%: - ekvivalent ##

Indikatori kraja reda biće zamenjeni izuzev znaka za novi red. Svaka izvorna datoteka mora da se završi znakom za novi red ukoliko nije prazna.

2. Faza

Ako se iza znaka za novi red nalazi „\“ – obrnuta kosa crta, pretprocesor briše oba znaka. Pošto se pretprocesorska direktiva završava znakom za kraj reda, ovo brisanje omogućava da stavite obrnutu kosu crtu na kraj reda kako bi ste naredbu nastavili u sledećem redu.

3. Faza

Izvorna datoteka se razlaže na pretprocesorske tokene i na sekvence razmaka. Tokeni su vam npr:

Printf

         (

               „Hello World. \n“

        )

        ;

 

Svaki komentar tumači se kao jedan razmak.

4. Faza

Izvršavaju se pretprocesorske direktive i proširuju pozivi makroa. Makro vam je u stvari naredba, prečica, automatizovana procedura ili radnja. Postoje tasterski i programerski makroi. Tasterski makroi beleže i reprodukuju akcije pritiska na taster ili kretanje miša, dok programerski makroi su u suštini programirana rutina.

Sve 4 faze, primenjuju se za sve datoteke umetnute direktivom #include. Kad prevodilac izvrši pretprocesorske direktive, uklanja ih iz svoje radne kopije izvornog koda.

5. Faza

Svi znakovi i izlazne sekvence u znakovnim konstantama i literalima znakovnih nizova, pretvaraju se u odgovarajuće znakove iz izvršnog skupa znakova.

6. Faza

Susedni literali se međusobno nadovezuju i spajaju u jedan znakovni niz.

7. Faza

Odvija se prevođenje, prevodilac analizira sekvencu tokena i generiše odgovarajući mašinski kod.

8. Faza

Povezivač izdvaja reference na spoljne objekte i funkcije i generiše izvornu datoteku. Ako se u modulu poziva spoljni objekat ili funkcija koji nisu definisani ni u jednoj jedinici za prevođenje, povezivač ih uzima iz standardne biblioteke. Spoljni objekti i funkcije smeju se definisati samo u jednom programu.


( Proces kompajliranja kod novijih programskih jezika )

Na kraju bi dodao da pretprocesor je zaseban program i on može da obavlja samo faze pretprocesiranje tj. faze od 1 do 4. U novijim programskim jezicima procesi su drugačiji jer pored leksičkih analiza, pretprocesiranja, parsiranja, semantički analiza, generišu i optimizuju kôd. Nadam se da vam je posle ovog posta jasnije kako se kompajlira C program. 


 

  

 

 

 

 

 

 

 

 

 

 

 

 

    

 

Nema komentara:

Objavi komentar