90 likes | 200 Views
Programare in limbajul C – Cursul 10 Preprocesorul C Prof. univ. dr. Constantin Popescu. Agenda. Includerea fişierelor Macrodefiniţii şi substituţii Compilare condiţionată. Preprocesarea.
E N D
Programare in limbajul C – Cursul 10Preprocesorul CProf. univ. dr. Constantin Popescu
Agenda • Includerea fişierelor • Macrodefiniţii şi substituţii • Compilare condiţionată
Preprocesarea • Preprocesarea este o fază de traducere a codului sursă care este aplicată înainte de compilare. Preprocesorul execută substituţii de text în codul sursă în trei moduri: • Includerea fişierelor: introducerea conţinutului unui alt fişier în fişierul sursă, ca şi cum l-am fi scris în acel fişier. • Macrosubstituţii: înlocuirea unei secvenţe de text cu alta. • Compilare condiţionată: în funcţie de unele circumstanţe, unele părţi ale codului sursă sunt (sau nu sunt) luate în considerare de compilator. • În primul rând, comenzile pentru preprocesor se scriu pe o singură linie. Preprocesorul nu cunoaşte sintaxa limbajului C - funcţii, instrucţiuni sau expresii. • Cu ajutorul preprocesorului putem cu foarte multă uşurinţă să transformăm un text sursă care nu seamănă cu un program C într-un program C.
Includerea fişierelor • O linie de forma #include <numefisier.h> • sau #include "numefisier.h" • va determina citirea conţinutului fişierului numefiser.h, adăugarea lui în fişierul curent şi compilarea acelui conţinut la un moment dat. • De exemplu, presupunem că nu mai vrem să scriem de fiecare dată la începutul fiecărui fişier sursă, prototipurile funcţiilor externe cum este: extern int getline(char [], int); • Am putea plasa prototipul într-un fişier header, care poate avea numele getline.h, iar acest fişier poate fi inclus la începutul fiecărui fişier sursă care utilizează funcţia getline, folosind directiva: #include "getline.h"
Macrodefiniţii şi substituţii (1) • O linie pentru preprocesor de forma: #define numetext • defineşte un macro cu un nume dat, care are ca valoare textul care este dat. • După această definiţie, peste tot unde apare acel nume, preprocesorul îl va înlocui cu textul de substituţie. • O soluţie mult mai bună este folosirea macro-definiţiilor: #define MAXLINE 100 char line[MAXLINE]; ... getline(line, MAXLINE); • De exemplu, presupunem că scriem următoarele macro-uri: #define A 2 #define B 3 #define C A + B • Mai târziu putem scrie definiţia: int x = C * 2;
Macrodefiniţii şi substituţii (2) • Dacă A, B şi C ar fi variabile obişnuite, atunci x ar lua val. 10. • Preprocesorul întotdeauna substituie textul macroului exact aşa cum a fost scris. Aşa că prima dată se va substitui textul pentru macro-ul C, rezultând: int x = A + B * 2; • După această substituţie vor fi înlocuite şi macro-urile A şi B ceea ce va da: int x = 2 + 3 * 2; • Doar când preprocesorul a terminat de făcut toate substituţiile va intra în acţiune compilatorul. Când este evaluată expresia de mai sus, rezultatul va fi că x se iniţializează cu 8! • Pentru a evita astfel de probleme, este o idee bună să includem paranteze explicite în definiţiile macrourilor care conţin expresii. Dacă definim macroul C ca: #define C (A + B) • Atunci declaraţia lui x se va extinde la: int x = (2 + 3) * 2; • Ceea ce va iniţializa valoarea variabilei x la 10, aşa cum ne-am propus iniţial.
Exemplu-Macrodefiniţii şi substituţii • Program pentru contorizarea liniilor, cuvintelor şi caracterelor: #include <stdio.h> #define DA 1 #define NU 0 int main() { int c,nl,nw,nc,inword; inword=NU; nl=nw=nc=0; while((c=getchar())!=EOF) { ++nc; if(c==’\n’) ++nl; if(c==’’||c==’\n’||c==’\t’) inword=NU; else if(inword==NU) { inword=DA; ++nw; } } printf("%d %d %d\n", nl,nw,nc); return 0; }
Conversie din litera mica in litera mare #include <stdio.h> #include <ctype.h> #define uppercase(x,n) for (i = 0; i < n; i++) x[i] = toupper(x[i]) int main () { int i; char a[6] = {'h', 'e', 'l', 'l', 'o', '\0'}; printf ("a lowercase: %s\n", a); uppercase(a,6); printf ("a uppercase: %s\n",a); return 0; }
Compilare condiţionată-directiva #ifdef • Dacă avem următoarea secvenţă: #ifdef nume textul programului #else textul programului #endif • Codul ce este compilat depinde de existenţa macroului nume. Dacă acest macro este definit (adică am folosit linia #define nume), atunci se compilează textul programului cuprins între #ifdef şi #else iar textul dintre #else şi #endif este ignorat. • Dacă macroul nu este definit atunci se va compila textul sursă cuprins între #else şi #endif, textul cuprins între #ifdef şi #else fiind ignorat. • Aceasta seamănă cu o instrucţiune if, dar se comportă total diferit: o instrucţiune if controlează care instrucţiune se execută în momentul rulării programului, pe când #ifdef controlează care parte a programului este compilată.