Sunteți pe pagina 1din 11

1.3.

Paşii de compilare ai unui program

1.3.1. Fazele de compilare


Fazele de compilare reprezinta principalele etape care au loc pâna la obtinerea codului
final executabil. Aceste etape sunt transparente pentru programator în majoritatea
compilatoarelor sau mediilor integrate, nefiind necesara activarea lor separata.
Compilatoarele traduc instructiunile dintr-un limbaj de programare de nivel inalt in limbaj
masina. Pentru aceasta sunt responsabile trei componente diferite:
 Preprocesorul – preprocesare;
 Compilatorul – compilare;
 Link-editorul – link-editare;

 Preprocesorul - Preprocesarea
Aceasta este prima faza în obtinerea codului executabil. În aceasta etapa au loc anumite
transformari asupra codului sursa, rezultând un fisier intermediar tot în limbajul C.
Principalele operatii care au loc în aceasta faza sunt prelucrarea directivelor de preprocesare
(liniile care încep cu caracterul #): expandarea macrodefinitiilor si includerea fisierelor.
Expandarea macrodefinitiilor presupune înlocuirea textuala a macro-ului cu valoarea asociata.
De exemplu, pentru definitia #define MAX 10 se va înlocui în textul sursa fiecare aparitie a
sirului MAX cu numarul 10.

În faza de includere a fisierelor, toate fisierele specificate de directivele #include vor fi incluse
textual în codul sursa al programului.

Orice mediu de proiectare C are un preprocessor. Asa cum sugereaza si numele, preprocesorul
parcurge programul inainte ca acesta sa fie procesat de compilator. Preprocesorul citeste un fisier
cod sursa linie cu line si executa fiecare directive de preprocesare pe care o intalneste.
Preprocesorul nu intelege limbajul de programare C. Aceasta poate fi sursa unor mari probleme
pentru programatori intrucat se pot rata cu usurinta probleme cauzate de trecerea preprocesorului
peste comenzi invalide. Doua erori dintre cele mai intalnite ar fi: includerea “;” la sfarsitul unei
macro definitii si plasarea unui comentariu pe aceeasi linie cu o directiva. Din moment ce
preprocesorul nu intelege interpretarea in C a “;” sau a comentariilor, acesta le va citi ca facand
parte din directive.

Sintaxa directivei de preprocesare:


Orice linie a codului sursa ce incepe cu caracterul # este o comanda spre preprocesor si se
numeste directiva de preprocesare. Este bine ca aceastea sa fie deplasate putin la stanga in cadrul
programului pentru a le distinge de codul C.
Caracterul # trebuie sa fie primul caracter (dupa spatiu) intr-o directiva de preprocesare. Cand o
linie incepe cu caracterul #, preprocesorul considera ca intreaga linie face parte din aceeasi
directiva. Pentru a continua o directiva si pe linia urmatoare, se foloseste caracterul de
continuitate “\” la sfarsitul liniei. Cand apare acest caracter, preprocesorul ataseaza continutul
liniei urmatoare la sfarsitul liniei curente.
Spre deosebire de compilatorul C, spatiile albe sunt foarte importante pentru preprocesor. De
exemplu, in C urmatoarele definitii sunt corecte:
int smallest (int arg1, int arg2);
int largest (int arg1, int arg2);
In cazul preprocesorului, doar unul din urmatoarele doua macrouri se executa dupa asteptari:
#define SMALLEST (arg 1, arg 2) ((arg1)<(arg2) ? (arg1) : (arg2))
#define LARGEST (arg1, arg2) ((arg1)<(arg2) ? (arg1) : (arg2))
SMALLEST se defineste ca un macro obiect sau ca o constanta simbolica, nu ca o functie macro
Ca Largest asa cum s-ar fi asteptat. Prin urmare, un apel pentru SMALLEST va fi extins de catre
procesor astfel:
(arg1, arg2) ((arg1)<(arg2) ? (arg1) : (arg2)) (oneInt, twoInt);

Includerea fisierelor:
Directivele #include instruieste preprocesorul sa inlocuiasca directivele cu continutul unui fisier
specificat. Acel fisier nu trebuie sa contina cod sursa C; de exemplu acesta poate sa contina doar
directive de preprocesare. In programele sistemelor incorporate, un fisier header ce descrie
resursele hardware-ului tinta de obicei include:
#include <machine.h>
Cand preprocesorul vede aceasta directiva, acesta va cauta fisierul machine.h si va inlocui
directiva cu continutul fisierului machine.h. Apoi preprocesorul va continua sa caute prin codul
sursa. Urmatoarea linie pe care o va citi va fi prima linie din fisierul machine.h.
Daca preprocesorul nu poate gasi fisierul specificat, acesta va trimite o eroare si va renunta
prelucrarea.

Cautarea includerii de fisiere:


<filename.h>
Daca numele fisierului este inclus intre paranteze unghiulare preprocesorul va cauta fisierul într-
o locație dependentă de sistem determinata de compilatorul folosit.
In general, parantezele unghiulare determina doua tipuri de cautari. In unele sisteme,
preprocesorul va cauta intr-un director sau intr-o lista de directoare care au fost specificate ca ar
contine libraria si fisiere header pentru compilator. In alte sisteme, preprocesorul va cauta intr-un
director sau intr-o lista de directoare specificate in mediul sistemului de operare ca o locatie
pentru comenzi.
“filename.h”
Daca numele fisierului este inclus intre ghilimele, comportamentul preprocesorului va fi mai
complex.
1) Preprocesorul cauta fisierul intr-o locatie dependenta de sistem. Aceasta poate fi aceeasi
locatie care se foloseste si pentru incluziunea < > (de obicei nu este aceeasi). Daca
preprocesorul cauta fisierele ce trebuie incluse intr-o singura locatie, atunci inseamna ca
acesta nu suporta incluziunile de tipul “ “ si le trateaza pe toate ca incluziuni de tipul < >.
2) Daca fisierul nu este gasit, preprocesorul va reincerca directiva ca si cum fisierul ar fi
inclus intre paranteze unghiulare.
In general in practica, ghilimelele determina preprocesorul sa caute fisierul in acelasi loc in care
este si fisierul cod sursa ce contine directiva.

Definirea constantelor simbolice:


Directiva #define comunica preprocesorului sa creeze o constanta simbolica.
#define MAXINT +32768
Directiva creeaza o constanta simbolica MAXINT si o asociaza cu valoarea +32768. Atunci cand
preprocesorul ajunge la directiva #define, acesta plaseaza MAXINT in lista simbolurilor definite.
Preprocesorul va inlocui MAXINT cu valoarea definita, in orice linie, ulterioara declararii, ce
contine simbolul MAXINT.
Asocierea acestei constante simbolice cu valoarea nu trece mai departecatre compilator. Cand
compilatorul examineaza fisierul sursa, simbolul MAXINT nu apare – preprocesorul l-a inlocuit
cu valoarea asociata. Simbolul este plasat intai in codul sursa si apoi este extins.
Exista doua motive principale pentru care constantele simbolice sunt folositoare:
Constantele simbolice clarifica codul sursa ambiguu:
Se poate plasa un cuvant plin de inteles in codul sursa in locul unei valori ambigue. De exemplu,
numarul 3.0e+5 poate sa nu fie clar. Presupunem ca introducem urmatoarea directiva:
#define LIGHTSPEEDkps 3.0e+5
Se poate observa ca simbolul are mai mult inteles in cod decat valoarea.
Constantele simbolice faciliteaza intretinerea codului:
Constantele simbolice, ca si variabilele, reduc erorile de scriere. Odata ce MAXINT este definit,
valoarea lui este atribuita intr-o singura locatie in codul sursa. Daca se doreste schimbarea valorii
MAXINT trebuie editata doar directiva #define si apoi recompilat codul. Fara directiva ar fi
trebuit schimbata valoarea la fiecare aparitie in program. Alte probleme se intalnescdaca aceeasi
valoare are intelesuri diferite.

Directiva #undef:
Se poate dori redefinirea unei constante simbolice. Preprocesorul poate da eroare daca se
defineste un simbol ce a fost deja definit.
Trebuie sa se specifice preprocesorului sa mute simbolul din lista sa inainte de redefinire:
#undef MAXINT
#define MAXINT +127
De exemplu se presupune ca avem un set de functii pentru care se doresc 8 biti portabili, in timp
ce au mai ramas cateva functii ce folosesc valori de tip int pe 16 biti. Se vor folosi urmatoarele
directive:
1) Se defineste MAXINT pentru 16 biti #define MAXINT +32768
2) Stergere continut MAXINT #undef MAXINT
3) Definire MAXINT pentru 8 biti #define MAXINT +127
NEdefinirea unui simbol nu are nici un efect daca simbolul nu a fost definit, preprocesorul ignora
directiva #undef.
Definirea simbolurilor “goale”:
O alta caracteristica folositoare a constantelor simbolice este aceea ca nu este obligatoriu ca ele
sa fie definite cu o valoare asociata. De exemplu:
#define 8BITINT
Aceasta directiva determina preprocesorul sa adauge simbolul 8BITINT in lista sa de simboluri
fara nici o valoare asociata. Daca se foloseste simbolul in cod acesta va fi inlocuit cu nimic.
Acesta conduce usor la erori de compilare.

Definirea macro functiilor:


Macro-urile sunt definite folosind directivele #define.
O macro functie este un macro inlocuitor cu o lista de argumente. Cand preprocesorul intalneste
o macro referinta acesta realizeaza o inlocuire de text si retine argumentele enumerate in codul
sursa.
Exemplu: #define SMALLEST (arg 1, arg 2) ((arg1)<(arg2) ? (arg1) : (arg2))
//codul programului
someInt=SMALLEST(oneValue, twoValue); //apelare macro
#define-creeaza un macro cu numele SMALLEST ce returneaza cea mai mica valoare dintre cele
doua argumente. Linia ce apeleaza functia macro, dupa ce a fostprocesata de preprocesor arata in
felul urmator:
someInt = ((oneValue) < (twoValue) ? (oneValue) : (twoValue));

Expansiune macro:
Argumentele unei macro functii pot fi expresii. Exista diferenta intre a trece expresii ca argument
a unei functii macro si de a trece ca argumente a unei functii. Cand treci o expresie la o functie,
expresia este intai evaluata si valoarea rezultata este primita de functie; in cazul unei maco
functii nu se evalueaza expresia. Din acest motiv se indica folosirea macro-urilor cu atentie.
Exemplu de macro eroare:
#define SQUARE(x) x * x
Se considera urmatorul apel pentru a apela SQUARE:
someInt = SQUARE (a+1); //inainte de axpansiune
someInt = a+1 * a+1; //dupa expansiune
Conform regulilor de prioritate a limbajului C se obtine un rezultate neasteptat pentru acest
calcul. Folosirea parantezelor este importanta intr-o macro definitie ce foloseste expresii. O
definire potrivita pentru SQUARE este urmatoarea:
#define SQUARE (x) ((x) * (x))

Operatorii # si ##:
Se folosesc pentru a extinde parametrii din interiorul ghilimelelor.

Codul sursa conditional:


Preprocesorul suporta directivele ce permit compilarea conditionala a codului sursa. Se poate
trece o portiune de cod intre paranteze, iar procesorul va decide daca aceasta portiune trece sau
nu in codul pentru compilare.
Directivele #if si #endif:
Directivele #if si #endif includ cod cand expresia #if se evalueaza la o valoare intreaga nenula:
#define DEBUG 1
#if DEBUG
#include <debug.h>
#endif
Astefl de blocuri sunt folosite des pentru a produce o depanare si versiunea finala a unui
program. Prima linie defineste simbolul DEBUG cu valoarea 1. Directiva #if testeaza sa vada
daca expresia argument are o valoare constanta intreaga nenula. Cand DEBUG are o valoare
nenula, preprocesorul va #include un fisier header creat pentru depanare numit debug.h.
Deoarece #if accepta o expresie ca argument, se poate verifica si astfel:
#define DEBUG_STATE 1
#if DEBUG_STATE = = 1
#include <debug1.h>
#endif

Functia defined():
Expresia constanta intreaga testata de #if nu poate contine functia sizeof(), distributii de tip sau
constante de tip enum. Se poate folosi functia defined() cu directive #if. Functia defined() va
returna 1 daca argumentul sau este un simbol definit , iar in cazul in care simbolul nu este definit
va returna 0. Vom putea rescrie exemplul de mai sus astfel:
#define DEBUG
#id defined (DEBUG)
#include <debug.h>
#endif
De asemenea se poate folosi !defined() pentru a verifica daca un simbol nu a fost definit. Acesta
va returna 1 daca argumentul este un simbol nedefinit si 0 daca este definit:
#if !defined (DEBUG)
#include <machine.h>
#endif

Directivele #else si #elif:


Preprocesorul C are abilitatea de a alege intre doua blocuri de compilare folosind directiva #else.
De exemplu, presupunem ca fisierul header de depanare include descrieri ale resurselor tinta.
Pentru a evita includerea acestora de doua ori, se poate scrie astfel:
#define DEBUG 1
#if DEBUG = = 1
#include <debug.h>
#else
#include <machine.h>
#endif
Daca se doreste a se crea o structura tip switch pentru blocurile de compilare, se va folosi
directiva #elif in interiorul unei perechi #if #endif. Se pot folosi atatea directive #elif cate sunt
necesare dar nu poate exista decat o directiva #else, ce trebuie sa urmeze directivei #elif.
#define STATE DEBUG
#if STATE = = DEBUG
#include <debug.h>
#elif STATE = = TESTING
#include <testing.h>
#elif STATE = = RELEASE
#include <machine.h>
#endif

Directivele #ifdef si #ifndef:


Daca nu se folosesc operatorii defined sau !defined intr-o directiva, atunci se pot folosi
directivele #ifdef sau #ifndef. #ifdef FOO este echivalent cu #if defined (FOO), iar #ifndef FOO
este echivalent cu #if !defined FOO:
#define DEBUG
#ifdef DEBUG
#include <debug.h>
#endif
#ifndef DEBUG
#include <machine.h>
#endif

Producerea mesajelor de eroare:


Directivele #error opresc preprocesorul si produc un mesaj de eroare specific. Majoritatea
compilatoarelor furnizeaza informatii aditionale odata cu mesajul, cum ar fi numele fisierului
sursa si pozitia directivei eroare in cadrul fisierului:
#if STATE = = DEBUG
#include <debug.h>
#elif STATE = = RELEASE
#include <machine.h>
#else
#error Bad or missing STATE value: need DEBUG or RELEASE
#endif

Definirea hardware-ului tinta:


Mediul de programare standard C permite definirea extensiilor specifice compilatorului cu
directiva de preprocesare #pragma. Preprocesorul poate interactiona cu aceste directive #pragma
in codul sursa sau poate fi compilatorul cel care actioneaza asupra acestor directive.
Directiva #pragma este folosita cel mai des in dezvoltarea embedded pentru a descrie resursele
specifice a hardware-ului tinta folosit cum ar fi memoria disponibila, porturile si setul de
instructiuni specializate.
 Compilatorul - Compilarea propriu-zisa
În faza de compilare, codul sursa al programului este transformat în cod obiect (un cod
intermediar specific compilatorului de C). Dupa compilare, se creeaza un fisier cu acelasi nume,
dar cu extensia .obj. În aceasta faza sunt semnalate posibilele erori sintactice din codul sursa.
Compilatorul traduce un program intr-o forma intermediara ce contine si cod masina si
informatii despre continutul programului. Compilatorul are cel mai important rol: proceseaza si
traduce programul intr-un limbaj, pe care calculatorul destinatie il intelege.

Pentru automatizarea sarcinilor se folosesc fisiere makefile. Structura tipică Makefile:


# Comments use the hash symbol
target: dependencies
command 1
command 2
.
.
command n
unde:
-target = numele unui fisier ce trebuie generat sau numele unei actiuni
-dependencies = lista de actiuni (fisiere) ce trebuie indeplinite (ce trebuie sa existe) pentru
a se realiza target-ul – make determina daca trebuie re-executat target-ul daca una
dintre dependete s-a modificat (unul din fisierele din lista de dependente s-a
modificat)
-command = comanda ce duce la realizarea target-ului (de obicei la comenzi sunt trecute comenzile
de compilare care duc la realizarea target-ului)
Regulile formeaza un lant de dependente. Pentru ca un target sa fie facut, se verifica daca toate
dependentele lui sunt facute.
Exemplu:
$ cat Makefile
all: test
test: test.o
gcc -o test test.o
test.o: test.c
gcc -Wall -c -o test.o test.c
$ make
gcc -Wall -c -o test.o test.c
gcc -o test test.o
$ ./test
Hello, World!
Liniile care au in componentă ":" se numesc linii de dependenţă. Ceea ce se află la stânga de ":"
reprezintă numele dependentei (sau a regulii), ceea ce se află la dreapta reprezintă regulile de
care depinde regula curenta.
La rularea make se caută prima regulă – se obişnuieşte să fie all;
1. all depinde de test
2. test depinde de test.o
3. test.o depinde de test.c
• test.c există
• se rulează comanda de compilare
• se obţine test.o
4. test.o a fost obţinut
• se rulează comanda de linking
• se obţine test
5. test este obţinut; all este obţinut

$ cat Makefile1
CC = gcc
CFLAGS = -Wall
all: test
test: test.o
$(CC) -o $@ $^
test.o: test.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
-rm -f *~ *.o test
$ make -f Makefile1 clean
rm -f * ~ *.o test
$ make -f Makefile1
gcc -Wall -c -o test.o test.c
gcc -o test test.o

Se definesc variabilele:
– CC, CFLAGS
– se referă cu $(nume_variabila) -> $(CC)
Variabile automate:
– $@ -> ţinta (target-ul)
– $^ -> toate dependinţele
– $< -> prima dependinţă
Regulă de ştergere: clean
– se şterg fişiere obiect, temporare şi executabilul
Opţiunea -f specifică un fişier Makefile altul decât cel implicit (Makefile sau GNUMakefile).
Un toolchain este un set de instrumente de programare ce este folosit sa efectueze o sarcină de
dezvoltare software complexă sau sa creeze un produs software, care este de obicei un alt
program sau un set de programe conexe. În general, instrumentele care formează un toolchain
sunt executate consecutiv astefl incat iesirea sau starea de mediu rezultat a fiecarui instrument
devine intrarea sau mediul de pornire pentru urmatorul, dar termenul de toolchain este de
asemenea folosit când se referă la un set de instrumente conexe care nu sunt executate neaparat
consecutiv.
Un toolchain simplu de dezvoltare software poate fi format dintr-un compilator si un link-er
(care transforma codul sursa intr-un program executabil), biblioteci (care oferă interfețe pentru
sistemul de operare) si un depanator (care este folosit pentru testarea si depanarea programelor
create).

 Linkeditare (editarea legaturilor)


"Leaga" între ele mai multe fisiere obiect si creeaza fisierul executabil. Aceasta faza este
importanta pentru programe structurate pe mai multe fisiere, dar si pentru programe constând
dintr-un singur fisier (pentru legarea fisierelor obiect ale bibliotecilor de functii specificate prin
#include).

Linker script:
Linker-ul combina fisierele de intrare intr-un singur fisier de iesire. Fisierul de iesire si fiecare
fisier de intrare se gasesc intr-un format special cunoscut sub numele de format obiect. Fiecare
fisier este numit fisier obiect. Fisierul de iesire este deseori numit executabil. Fiecare fisier obiect
contine o lista de sectiuni. Fiecare sectiune dintr-un fisier obiect are are un nume si o
dimensiune. Majoritatea sectiunilor au asociat cate un bloc de date, cunoscut ca si continutul
sectiunii. O sectiune poate fi marcata ca incarcabila, ceea ce inseamna ca, continutul trebuie
incarcat in memorie cand fisierul de iesire este rulat. O sectiune fara continut poate fi alocata,
ceea ce inseamna ca o zona din memorie trebuie rezervata. O sectiune ce nu este incarcabila, nici
alocabila, de obicei contine anumite informtii de depanare.
Orice sectiune de iesire incarcabila sau alocabila are doua adrese. Prima adresa este VMA,
adresa memoriei virtuale. Aceasta este adresa pe care sectiunea o va avea cand fisierul de iesire
este rulat. A doua adresa este LMA sau adresa memorie de incarcare. Aceasta este adresa la care
sectiunea va fi incarcata. Fiecare fisier obiect are de asemenea o lista de simboluri, cunoscut sub
numele de tabel de simboluri. Se pot vedea simbolurile intr-un fisier obiect folosind programul
nm sau objdump cu optiunea ‘-t’.

Formatul unui Linker script – fisier text.


Un fisier linker script este format dintr-o serie de comenzi, separate de “;”. Spatiile albe sunt
ignorate. Se pot include comentarii ca si in C, delimitate de ‘/*’ si ‘*/’.

Exemplu de linker script:


Cel mai simplu linker script contine o singura comanda: ‘SECTIONS’. Aceasta comanda se
foloseste pentru a descrie structura memoriei fisierului de iesire.
Urmatorul program este format doar din cod, data initializata si data neinitializata. Acestea vor fi
in sectiunile ‘.text’, ‘.data’ si ‘.bss’. Vom considera ca aceste sectiuni sunt singurele care apar in
fisierul de intrare.
Pentru acest exemplu vom considera ca, codul trebuie incarcat la adresa 0x10000 si data ar trebui
sa inceapa la adresa 0x8000000. Urmatorul linker script va realiza acest lucru:
SECTIONS
{
. = 0x10000;
.text : { *(.text) }
. = 0x8000000;
.data : { *(.data) }
.bss : { *(.bss) }
}
Prima linie a comenzii ‘SECTIONS’ seteaza valoarea simbolului special ‘.’, care este
contorul locatiei. Daca nu se specifica adresa unei sectiuni de iesire altfel, adresa va fi setata de
la valoarea curenta a contorului locatiei. Contorul locatiei va fi incrementat apoi cu dimensiunea
sectiunii de iesire. La inceputul comenzii ‘SECTIONS’ contorul locatiei are valoarea ‘0’.
A doua linie defineste o sectiune de iesire, ‘.text’. Sintaxa ‘:’ este o sintaxa necesara, dar
care poate fi ignorata acum. In interiorul acoladelor de dupa numele sectiunii de iesire, se trec
numele sectiunilor de intrare care ar trebui plasate in aceasta sectiune de iesire. ‘*’ este un
inlocuitor care se potriveste cu orice nume de fisier. Expresia ‘*(.text)’ inseamna: toate sectiunile
de intrare ‘.text’ in toate fisierele de intrare.
Din moment ce contorul locatiei este ‘0x10000’ cand sectiunea de iesire ‘.txt’ este
definita, linker-ul va seta adresa sectiunii ‘.text’ in fisierul de iesire la ‘0x10000’.
Liniile ramase definesc sectiunile ‘.data’ si ‘.bss’ in fisierul de iesire. Linker-ul va plasa
sectiunea de iesire ‘.data’ la adresa ‘0x8000000’. Dupa ce linker-ul va plasa sectiunea de iesire
‘.data’, valoarea contorului de locatie va fi ‘0x8000000’ plus dimensiunea sectiunii de iesire
‘.data’. Efectul este acela ca linker-ul va plasa in memorie sectiunea de iesire ‘.bss’ imediat dupa
sectiunea de iesire ‘.data’.

Mai multe informatii despre linker script:


http://www.emprog.com/support/documentation/thunderbench-Linker-Script-guide.pdf

http://hertaville.com/a-sample-linker-script.html

Fisier .map:
Un fisier .map contine informatii despre link ce pot fi generate doar de linker. Un fisier .map este
folositor pentru monitorizarea dimensiunilor codului si datei. De asemenea arata unde sunt
incarcate modulele si si care module au fost incarcate din librarii. Pentru a obtine un fisier .map
se poate scrie comanda -Wl,-Map,demo.map in linia de comanda.
Mai multe informatii:
http://www.atmel.com/webdoc/AVRLibcReferenceManual/
group__demo__project_1demo_project_map.html
1.3.2. Fişiere header
Un fişier header este un fişier aflat în sistem sau creat de către programator, care conţine
declaraţii şi definiţii de funcţii şi variabile.
Acestea conţin o colecţie de declaraţii de funcţii, grupate după funcţionalitatea pe care ele o
oferă. Atunci când includem un fişier header (.h) într-un fişier sursă (.c), compilatorul va
cunoaşte toate semnăturile funcţiilor de care are nevoie, şi va fi în stare să genereze codul obiect
pentru fiecare fişier sursă în parte.

1.3.3. Procesul de linkeditare


Dupa ce programul sursă a fost translatat în program obiect, el este supus operaţiei de
linkeditare. Scopul fazei de linkeditare este acela de a obţine o formă finală a programului, în
vederea execuţiei acestuia. Linkeditorul “leagă” modulele obiect, rezolvă referinţele către
funcţiile externe şi rutinele din biblioteci şi produce cod executabil, memorat într-un alt fisier,
numit fişier executabil (acelaşi nume, extensia .exe).

1.3.4. Crearea unui fişier executabil


Etapele necesare obtinerii unui fisier executabil sunt urmatoarele:

1.3.5. Depanarea unui program scris în limbajul C


Este o activitate de dezvoltare care identifica cauza unui defect, repara codul si verifica daca
defectul a fost fixat corect. Pentru aceasta este foarte importanta abilitatea programatorului de a
identifica bug-urile dar de asemenea exista si metodele de depanare (debugging).
Pentru programele scurte se poate folosi instructiunea printf pentru afisarea diverselor
informatii, chiar daca presupune încarcarea codului.
Pentru programele mai complicate este necesara folosirea unor instrumente de depanare
(debuggers). Acestea sunt programe sau biblioteci ce-i permit programatorului monitorizarea
executiei unui program, oferindu-i capacitatea de a-l porni, opri, reporni si chiar de a rula înapoi.
De asemenea se mai poate executa codul linie cu linie pentru a detecta eroarea (cu F5). Exista
numeroase metode de depanare specifice fiecarui limbaj de programare.

1.3.6. Evaluarea şi rularea unui program scris în limbajul C


Evaluarea si rularea unui program scris in limbajul C se face astfel: se ia fiecare instructiune
în ordinea scrierii în program, limbajul C fiind unul secvential.

S-ar putea să vă placă și