logo

Compilarea unui program C: în culise

Compilarea este procesul de conversie a codului sursă al limbajului C în cod mașină. Deoarece C este un limbaj de nivel mediu, are nevoie de un compilator pentru a-l converti într-un cod executabil, astfel încât programul să poată fi rulat pe mașina noastră.

Programul C trece prin următoarele faze în timpul compilării:



procesul de compilare în c

Procesul de compilare în C

Cum compilăm și rulăm un program C?

Mai întâi avem nevoie de un compilator și un editor de cod pentru a compila și rula un program C. Exemplul de mai jos este al unei mașini Ubuntu cu compilator GCC.

Pasul 1: Crearea unui fișier sursă C

Mai întâi creăm un program C folosind un editor și salvăm fișierul ca filename.c



cum se returnează o matrice java
  $ vi filename.c>

Putem scrie un program simplu Hello World și îl putem salva.

Pasul 2: Compilarea folosind compilatorul GCC

Folosim următoarea comandă în terminal pentru compilarea fișierului sursă filename.c

  $ gcc filename.c –o filename>

Putem transmite multe instrucțiuni compilatorului GCC la diferite sarcini, cum ar fi:



  • Opțiunea -Wall activează toate mesajele de avertizare ale compilatorului. Această opțiune este recomandată pentru a genera un cod mai bun.
  • Opțiunea -o este folosită pentru a specifica numele fișierului de ieșire. Dacă nu folosim această opțiune, atunci este generat un fișier de ieșire cu numele a.out.

Dacă nu există erori în programul nostru C, va fi generat fișierul executabil al programului C.

Pasul 3: Executarea programului

După ce executabilul de compilare este generat și rulăm executabilul generat folosind comanda de mai jos.

întregul java în șir
  $ ./filename>

Programul va fi executat și rezultatul va fi afișat în terminal.

Ce se întâmplă în procesul de compilare?

Un compilator convertește un program C într-un executabil. Există patru faze pentru ca un program C să devină un executabil:

    Preprocesare Compilation Assembly Linking

Prin executarea comenzii de mai jos, obținem toate fișierele intermediare din directorul curent împreună cu executabilul.

  $gcc -Wall -save-temps filename.c –o filename>

Următoarea captură de ecran arată toate fișierele intermediare generate.

Fișiere intermediare

deschide un fișier cu java

Să vedem unul câte unul ce conțin aceste fișiere intermediare.

1. Preprocesare

Aceasta este prima fază prin care este trecut codul sursă. Această fază include:

  • Eliminarea comentariilor
  • Extinderea Macro-urilor
  • Extinderea fișierelor incluse.
  • Compilare condiționată

Ieșirea preprocesată este stocată în nume de fișier.i . Să vedem ce se află în interiorul filename.i: using $vi nume de fișier.i

seleniu

În rezultatul de mai sus, fișierul sursă este umplut cu o mulțime de informații, dar în cele din urmă, codul nostru este păstrat.

  • printf conține acum a + b, mai degrabă decât add(a, b), asta pentru că macrocomenzile s-au extins.
  • Comentariile sunt eliminate.
  • #include lipsește, în schimb vedem o mulțime de cod. Prin urmare, fișierele antet au fost extinse și incluse în fișierul sursă.

2. Compilarea

Următorul pas este să compilați filename.i și să produceți un; fișier intermediar de ieșire compilat nume de fișier.s . Acest fișier este în instrucțiuni la nivel de asamblare. Să vedem prin acest fișier folosind $nano nume de fișier.s comanda terminalului.

Fișierul codului de asamblare

Instantaneul arată că este în limbaj de asamblare, pe care asamblatorul îl poate înțelege.

3. Asamblare

În această fază, numele fișierului.s este luat ca intrare și transformat în nume de fișier.o de către asamblator. Acest fișier conține instrucțiuni la nivel de mașină. În această fază, numai codul existent este convertit în limbajul mașinii, iar apelurile de funcții precum printf() nu sunt rezolvate. Să vedem acest fișier folosind $vi nume de fișier.o

șiruri de caractere java

Cod binar

4. Legătura

Aceasta este faza finală în care se realizează toate legăturile apelurilor de funcții cu definițiile lor. Linker știe unde sunt implementate toate aceste funcții. Linker face, de asemenea, ceva în plus, adaugă un cod suplimentar programului nostru, care este necesar când programul începe și se termină. De exemplu, există un cod care este necesar pentru configurarea mediului, cum ar fi transmiterea argumentelor din linia de comandă. Această sarcină poate fi ușor verificată prin utilizarea $size filename.o și $size nume de fișier . Prin aceste comenzi, știm cum crește fișierul de ieșire de la un fișier obiect la un fișier executabil. Acest lucru se datorează codului suplimentar pe care Linker îl adaugă programului nostru.

Notă: GCC în mod implicit face legături dinamice, deci printf() este legat dinamic în programul de mai sus. Consultați aceasta, aceasta și aceasta pentru mai multe detalii despre legarea statică și dinamică.