Documente Academic
Documente Profesional
Documente Cultură
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.
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.
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.
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.
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
$ 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).
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’.
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.