Bibliografie selectivă recomandată
Burileanu, D., Dan, C., Pădure, M., Programare în C. Culegere de probleme, Editura Printech,
Bucureşti, 2004.
Costea, D., Iniţiere în limbajul C, Editura Teora, Bucureşti, 1995.
Cristea, V., Ciumale, C., Kalisz, E., Panoiu, A., Limbajul C standard, Editura Teora, Bucureşti,
1992.
Dan, C., Burileanu, D., Introducere în programarea calculatoarelor. Limbajul C, Editura
Printech, Bucureşti, 2001.
Kernigham, B.W., Ritchie, D.M., The C Programming Language, Pretice Hall.
Knuth, D.E., Tratat de programarea calculatoarelor – Algoritmi fundamentali, vol. I, Editura
Tehnică, Bucureşti, 1973.
Năstac, D.I. Programarea calculatoarelor în limbajul C – Elemente fundamentale, Editura
Printech, Bucureşti, 2006.
Negrescu, L., Limbajele C şi C++ pentru începători, Vol. I, Editura Microinformatica, Cluj-
Napoca, 1996.
Negrescu, L., Limbajul C – Culegere de probleme, Fascicolele 1-2, Cluj-Napoca, 1991.
Rusu, I., Gavrilescu, D., Grosu, V., Îndrumar de laborator pentru programarea calculatoarelor,
Editura MATRIX ROM, Bucureşti, 2004.
Rusu, I., Gavrilescu, D., Grosu, V., Programarea calculatoarelor în limbaj C, Editura MATRIX
ROM, Bucureşti, 2002.
1. Noţiuni de bază în programarea calculatoarelor
Programarea este o componentă a informaticii care are ca principal obiectiv realizarea de
programe care să constituie soluţiile oferite cu ajutorul calculatorului unor probleme date. Programatorii
sunt acele persoane apte să implementeze într-un limbaj de programare algoritmul propus ca soluţie la
respectiva problemă, ce se pretează a fi rezolvată cu ajutorul unui sistem de calcul. După nivelul de
implicare în efortul de rezolvare a problemelor specialiştii în programare pot fi împărţiţi în mai multe
categorii: analişti, analişti-programatori, ingineri-programatori, programatori amatori, etc. Cu toţii au
însă în comun faptul că fiecare trebuie să cunoască sintaxa şi semantica unui limbal de programare şi să
fie capabil, nu doar să citească, ci chiar să scrie “codul sursă”, adică programul propriu-zis. Din acest
punct de vedere cunoştinţele de programare sunt considerate “ABC-ul” informaticii şi sunt indispensabile
oricărui profesionist în domeniu şi nu numai.
Pentru a putea fi rezolvată cu ajutorul unui sistem de calcul, fiecare problemă va trebui să treacă
prin trei etape obligatorii: analiza problemei, proiectarea algoritmului de soluţionare şi implementarea
algoritmului într-un program pe calculator. În ultima etapă, sub acelaşi nume, au fost incluse în plus
două subetape cunoscute sub numele de testarea şi întreţinerea programului. Aceste subetape nu lipsesc
din “ciclul de viaţă” a oricărui produs-program.
Dacă etapa implementării algoritmului într-un program executabil este o etapă exclusiv practică,
realizată “în faţa calculatorului”, celelalte două etape au un pronunţat caracter teoretic. În consecinţă,
primele două etape sunt caracterizate de un anumit grad de abstractizare. Din punct de vedere practic
însă, şi în ultimă instanţă, criteriul decisiv ce conferă succesul rezolvării problemei este dat de calitatea
implementării propriu-zise. Mai exact, succesul soluţionării este dat de performanţele programului:
utilitate, viteză de execuţie, fiabilitate, posibilităţi de dezvoltare ulterioare, lizibilitate, etc. Cu toate
acestea este imatură şi neprofesională “strategia” programatorilor începători care, neglijînd primele două
etape, sar direct la a treia, evitând etapa de analiză şi componenta abstractă a efortului de soluţionare. Ei
se justifică cu toţii prin expresii puerile de genul: “Eu nu vreau să mai pierd vremea cu “teoria”, am să
fac programul cum ştiu eu. Câtă vreme nu va face altcineva altul mai bun decît al meu, nu am de ce să-
mi mai bat capul!”.
Stabilirea corectitudinii şi eficienţei soluţionării problemei de rezolvat
Este adevărat că etapa care vizează implementarea unei probleme este fundamentală, dar primele
două etape au o importanţă capitală. Ele sunt singurele ce pot oferi răspunsuri corecte la următoarele
întrebări dificile: Există certitudinea că soluţia găsită este şi cea corectă? Există certitudinea că
problema este rezolvată complet? Cât de eficientă este soluţia găsită? Cât de aproape este soluţia aleasă
de cea optimă?
Să menţionăm în plus că literatura de specialitate conţine un număr impresionant de probleme
“capcană” pentru începători, şi nu numai pentru ei. Ele provin majoritatea din realitatea imediată dar
pentru fiecare dintre ele nu se cunosc soluţii eficiente. De exemplu, este dovedit teoretic că problema,
“aparent banală” pentru un calculator, a proiectării Orarului optim într-o instituţie de învăţămînt
(facultate sau liceu) este o problemă intratabilă la ora actuală (toate programele care s-au realizat pînă
acum nu oferă decît soluţii aproximative fără a putea spune cât de aproape sau de departe este soluţia
optimă de orar).
Câţi dintre programatorii începători n-ar fi surprinşi să afle că problema “atât de simplă” (ca
enunţ), a cărei soluţionare tocmai au abandonat-o, este de fapt o problemă dovedită teoretic ca fiind
intratabilă sau chiar insolvabilă algoritmic? Partea proastă a lucrurilor este că problemele netratabile pot
fi cu uşurinţă confundate cu unele uşoare la o privire rapidă şi lipsită de experienţă.
Etapa de analiză este singura care permite dovedirea cu argumente riguroase a corectitudinii
soluţiei, iar etapa de proiectare este singura care poate oferi argumente precise în favoarea eficienţei
soluţiei propuse.
În general problemele concrete din informatică au în forma lor iniţială sau în enunţ o caracteristică
pragmatică, fiind foarte ancorate în realitatea imediată. Totuşi ele conţin în formularea lor iniţială un grad
mare de eterogenitate, diversitate şi lipsă de rigoare. Fiecare dintre aceste “defecte” este un obstacol
major pentru demonstrarea corectitudinii soluţiei. Rolul esenţial al etapei de analiză este acela de a
transfera problema într-un plan abstract, adică de a o modela. Acest “univers paralel abstract” este dotat
cu mai multă rigoare şi disciplină internă, avînd legi precise, şi poate oferi instrumentele logice şi formale
necesare pentru demonstrarea riguroasă a corectitudinii soluţiei problemei. Planul abstract în care sunt
“transportate” toate problemele de informatică este planul sau universul obiectelor matematice iar
corespondentul problemei în acest plan va fi modelul matematic abstract asociat problemei.
Demonstrarea corectitudinii proprietăţilor ce leagă obiectele universului matematic a fost şi este sarcina
matematicienilor. Celui ce analizează problema din punct de vedere informatic îi revine sarcina (nu
tocmai uşoară) de a dovedi printr-o demonstraţie constructivă că există o corespondenţă precisă între
părţile componente ale problemei reale, “dezasamblată” în timpul analizei, şi părţile componente ale
modelului abstract asociat. Odată descoperită, formulată precis şi dovedită, această “perfectă oglindire”
a problemei reale în planul obiectelor matematice oferă certitudinea că toate proprietăţile şi legăturile ce
există între subansamblele modelului abstract se vor regăsii precis (prin reflectare) între părţile interne
ale problemei reale, şi invers. Atunci, soluţiei abstracte descoperite cu ajutorul modelului matematic
abstract îi va corespunde o soluţie reală concretizată printr-un algoritm ce poate fi implementat într-un
program executabil.
Ideea centrală a etapei a doua – proiectarea unui algoritm de soluţionare eficient poate fi formulată
astfel: din studiul proprietăţilor şi limitelor modelului matematic abstract asociat problemei se deduc
limitele inferioare ale complexităţii minimale (“efortului minimal obligatoriu”) inerente oricărui
algoritm ce va soluţiona problema în cauză. Complexitatea internă a modelului abstract şi complexitatea
soluţiei abstracte se va reflecta imediat asupra complexităţii reale a algoritmului, adică asupra eficienţei
de soluţionare a problemei. Acest fapt permite prognosticarea încă din această fază – faza de proiectare
a algoritmului de soluţionare – a eficienţei practice, măsurabilă ca durată de execuţie, a programului.
Algoritmul
Se ştie că la baza oricărui program stă un algoritm (care, uneori, este numit metodă de rezolvare).
Noţiunea de algoritm este o noţiune fundamentală în informatică şi înţelegerea ei, alături de înţelegerea
modului de funcţionare a unui calculator, permite înţelegerea noţiunii de program executabil. Vom oferi
în continuare o definiţie unanim acceptată pentru noţiunea de algoritm:
Definiţie. Prin algoritm se înţelege o mulţime finită de operaţii (instrucţiuni) elementare care executate într-o
ordine bine stabilită (determinată), pornind de la un set de date de intrare dintr-un domeniu de valori posibile
(valide), produce în timp finit un set de date de ieşire (rezultate).
Cele trei caracteristici esenţiale ale unui algoritm sunt:
Determinismul – dat de faptul că ordinea de execuţie a instrucţiunilor algoritmului este bine precizată
(strict determinată). Acest fapt dă una din calităţile de bază a calculatorului: “el” va face întotdeauna
ceea ce i s-a cerut (prin program) să facă, “el” nu va avea iniţiative sau opţiuni proprii, “el” nu-şi
permite să greşească nici măcar odată, “el” nu se va plictisi ci va duce programul la acelaşi sfârşit
indiferent de câte ori i se va cere să repete acest lucru. Nu aceeaşi situaţie se întâmplă cu fiinţele umane
(Errare humanum est). Oamenii pot avea în situaţii determinate un comportament non-deterministic
(surprinzător). Acesta este motivul pentru care numeroşi utilizatori de calculatoare (de exemplu
contabilii), datorită fenomenului de personificare a calculatorului (confundarea acţiunilor şi dialogului
“simulat” de programul ce rulează pe calculator cu reacţiile unei personalităţi vii), nu recunosc
perfectul determinism ce stă la baza executării oricărui program pe calculator.
Universalitatea – dată de faptul că, privind algoritmul ca pe o metodă automată (mecanică) de
rezolvare, această metodă are un caracter general-universal. Algoritmul nu oferă o soluţie punctuală,
pentru un singur set de date de intrare, ci oferă soluţie pentru o mulţime foarte largă (de cele mai multe
ori infinită) de date de intrare valide. Aceasta este trăsătura de bază care explică deosebita utilitate a
calculatoarelor şi datorită acestei trăsături suntem siguri că investiţia financiară făcută prin cumpărarea
unui calculator şi a produsului software necesar va putea fi cu siguranţă amortizată. Cheltuiala se face
o singură dată în timp ce programul pe calculator va putea fi executat rapid şi economicos de un număr
oricât de mare de ori, pe date diferite!
De exemplu, algoritmul de rezolvare învăţată la liceu a ecuaţiilor de gradul doi: ax2+bx+c=0, se aplică
cu succes pentru o mulţime infinită de date de intrare: (a,b,c)\{0}xx.
Finitudinea – pentru fiecare intrare validă orice algoritm trebuie să conducă în timp finit (după un
număr finit de paşi) la un rezultat. Această caracteristică este analogă proprietăţii de convergenţă a
unor metode din matematică: trebuie să avem garanţia, dinainte de a aplica metoda (algoritmul), că
metoda se termină cu succes (ea converge către soluţie).
Să observăm şi diferenţa: în timp ce metoda matematică este corectă chiar dacă ea converge către
soluţie doar la infinit (!), un algoritm trebuie să întoarcă rezultatul după un număr finit de paşi. Să
observăm de asemenea că, acolo unde matematica nu oferă dovada, algoritmul nu va fi capabil să o
ofere nici el. De exemplu, nu este greu de scris un algoritm care să verifice corectitudinea afirmaţiei
Conjecturii lui Goldbach: “Orice număr par se scrie ca sumă de două numere prime”, dar, deşi
programul rezultat poate fi lăsat să ruleze pînă la valori extrem de mari, fără să apară nici un contra-
exemplu, totuşi conjectura nu poate fi astfel infirmată (dar nici afirmată!).
Descrierea algoritmilor
Două dintre metodele clasice de descriere a algoritmilor sunt Schemele logice şi Pseudo-Codul.
Ambele metode de descriere conţin doar patru operaţii (instrucţiuni) elementare care au fiecare un
corespondent atât schemă logică cât şi în pseudo-cod.
În cele ce urmează vom descrie doar varianta oferită de pseudo-cod deoarece folosirea schemelor
logice s-a redus drastic în ultimii ani. Schemele logice mai pot fi întâlnite sub numele de diagrame de
proces în anumite cărţi de specialitate inginereşti. Avantajul descrierii algoritmilor prin scheme logice
este dat de libertatea totală de înlănţuire a operaţiilor (practic, săgeata care descrie ordinea de execuţie,
pleacă de la o operaţie şi poate fi trasată înspre orice altă operaţie). Este demonstrat matematic riguros
că descrierea prin pseudo-cod, deşi pare mult mai restrictivă (operaţiile nu pot fi înlănţuite oricum, ci
trebuie executate în ordinea citirii: de sus în jos şi de la stînga la dreapta), este totuşi perfect echivalentă.
Deci, este dovedit că plusul de ordine, rigoare şi simplitate pe care îl oferă descrierea prin pseudo-cod nu
îngrădeşte prin nimic libertatea programării. Totuşi, programele scrise în limbajele de asamblare, care
sunt mult mai compacte şi au dimensiunile mult reduse, nu ar putea fi descrise altfel decît prin scheme
logice.
1. Atribuirea
var:=expresie;
2. Intrare/Ieşire
Citeşte var1, var2, var3, …;
Scrie var1, var2, var3, …; sau Scrie expresia1, expresia2, expresia3,…;
3. Condiţionala
Dacă <condiţie_logică> atunci instrucţiune1 [altfel instrucţiune2];
4. Ciclurile – Există (din motive de uşurinţă a descrierii algoritmilor) trei tipuri de instrucţiuni de
ciclare. Ele sunt echivalente între ele, oricare variantă de descriere putînd fi folosită în locul celorlalte
două, cu modificări sau adăugiri minimale:
repetă instrucţiune1, instrucţiune2, … pînă când <condiţie_logică>;
cât timp <condiţie_logică> execută instrucţiune;
pentru var_contor:=val_iniţială pînă la val_finală execută instrucţiune;
În cazul ciclurilor, grupul instrucţiunilor ce se repetă se numeşte corpul ciclului iar condiţia logică
care permite sau nu reluarea execuţiei ciclului este denumită condiţia de ciclare. Observăm că ciclul de
tipul Repetă are condiţia de repetare la sfârşit ceea ce are ca şi consecinţă faptul că, corpul ciclului se
execută cel puţin odată, în mod obligatoriu, înainte de verificarea condiţiei logice. Nu acelaşi lucru se
întâmplă în cazul ciclului de tipul cât timp, când este posibil ca instrucţiunea compusă din corpul ciclului
să nu poată fi executată nici măcar odată. În plus, să mai observăm că ciclul de tipul Pentru … pînă la
conţine (în mod ascuns) o instrucţiune de incrementare a variabilei contor.
Limbajele de programare care sunt relativ apropiate de limbajele naturale sunt denumite limbaje
de nivel înalt (high-level), de exemplu limbajul Pascal, spre deosebire de limbajele de programare mai
apropiate de codurile numerice ale instrucţiunilor microprocesorului. Acestea din urmă se numesc
limbaje de nivel scăzut (low-level), de exemplu limbajul de asamblare. Limbajul de programare C are un
statut mai special el putând fi privit, datorită structurii sale, ca făcînd parte din ambele categorii.
Peste tot unde în pseudo-cod apare cuvîntul instrucţiune el poate fi înlocuit cu oricare din cele
patru instrucţiuni elementare. Această substituire poartă numele de imbricare. Prin instrucţiune se va
înţelege atunci, fie o singură instrucţiune simplă (una din cele patru), fie o instrucţiune compusă.
Instrucţiunea compusă este formată dintr-un grup de instrucţiuni delimitate şi grupate în mod precis (între
acolade { } în C).
Spre deosebire de pseudo-cod care permite doar structurile noi formate prin imbricarea repetată
a celor patru instrucţiuni în modul precizat, schemele logice permit structurarea în orice succesiune a
celor patru instrucţiuni elementare, ordinea lor de execuţie fiind dată de sensul săgeţilor. Repetăm că
deşi, aparent, pseudo-codul limitează libertatea de descriere doar la structurile prezentate, o teoremă
fundamentală pentru programare afirmă că puterea de descriere a pseudo-limbajului este aceeaşi cu cea
a schemelor logice.
Forma de programare care se bazează doar pe cele patru structuri se numeşte programare
structurată (spre deosebire de programarea nestructurată bazată pe descrierea prin scheme logice).
Teorema de echivalenţă a puterii de descriere prin pseudo-cod cu puterea de descriere prin schemă
logică afirmă că programarea structurată (aparent limitată de cele patru structuri) este echivalentă cu
programarea nestructurată (liberă de structuri impuse). Evident, prin ordinea, lizibilitatea şi fiabilitatea
oferită de cele patru structuri elementare (şi asta fără a îngrădi libertatea de exprimare) programarea
structurată este net avantajoasă. În fapt, limbajele de programare nestructurată (Fortran, Basic) au fost
de mult scoase din uz, ele (limbajele de asamblare) sunt necesare a fi folosite în continuare doar în
programarea de sistem şi în programarea industrială (în automatizări).
Programul
Prin program se înţelege un şir de instrucţiuni-maşină care sunt rezultatul compilării algoritmului
proiectat spre rezolvarea problemei dorite ce a fost descris într-un limbaj de programare (ca şi cod sursă).
Etapele realizării unui program sunt:
editarea codului sursă, etapă ce se realizează cu ajutorul unui program editor de texte rezultatul fiind
un fişier C, cu extensia .c (.cpp);
compilarea, etapa de traducere din limbajul de programare C în limbajul intern al micro-procesorului,
şi este realizată cu ajutorul programului compilator C şi are ca rezultat un fişier obiect, cu extensia
.obj (în limbajul C) sau .exe (în limbajul Pascal);
link-editarea, etapă la care se adaugă modului obiect rezultat la compilare diferite module conţinînd
subprograme şi rutine de bibliotecă, rezultînd un fişier executabil (această etapă este comasată în
Turbo Pascal sau Borland Pascal cu etapa de compilare), cu extensia .exe
execuţia (Run), etapa de lansare în execuţie propriu-zisă a programului obţinut, lansare realizată de
interpretorul de comenzi al sistemului de operare (command.com pentru sistemele DOS+Windows)
Observăm că aceste etape sunt complet independente în timp unele de altele şi necesită pentru
limbajul C utilizarea a patru programe ajutătoare: editor de texte, compilator C, link-editor şi
interpretorul de comenzi al sistemului de operare. În cazul mediilor de programare integrate (Borland)
comandarea acestor patru programe ajutătoare precum şi depanarea erorilor de execuţie este mult
facilitată.
OBSERVAŢIE: Link-editarea reprezintă asamblarea unor module precompilate pentru a rezulta un fişier
executabil. De exemplu, dacă avem mai multe module de program (să zicem diverse funcţii) le putem
compila pe fiecare în mod separat şi în felul acesta putem observa şi erorile mai uşor iar la sfârşit le
linkedităm şi va rezulta programul. Fişierele precompilate (pe care le vom link-edita) au avantajul că se
pot folosi la diferite proiecte păstrând de exemplu o oarecare “paternitate” asupra lor, va fi foarte dificil
ca cineva să obţinţă codul sursă şi să-l poată modifica fără să depună un efort considerabil. Deci va putea
utiliza funcţiile dar e destul de dificil să modifici sau să copiezi într-un program personal doar câteva din
aceste funcţii. E mai uşoară şi depanarea programului pe module.
De asemenea, merită subliniat faptul că în timp ce fişierul text C, ce conţine codul sursă, poate fi
transportat pe orice maşină (calculator) indiferent de micro-procesorul acesteia urmând a fi compilat “la
faţa locului”, în cazul fişierului obiect acesta nu mai poate fi folosit decât pe maşina (calculatorul) pentru
care a fost creat (datorită instrucţiunilor specifice micro-procesorului din care este compus). Deci, pe
calculatoare diferite (avînd micro-procesoare diferite) vom avea nevoie de compilatoare C diferite.
În plus, să remarcăm faptul că fişierele obiect rezultate în urma compilării pot fi link-editate
împreună, chiar dacă provin din limbaje de programare diferite. Astfel, un program rezultat (un fişier
.exe sau .com) poate fi compus din module obiect care provin din surse diferite (fişiere Pascal, C,
asamblare, etc.).
2. Introducere în limbajul C – noţiuni de bază
Toate elementele care compun un limbaj de programare, aşa cum este şi limbajul C nu sunt de
sine stătătoare, ci în conjuncţie unul cu celălalt. Din acest motiv este important să fie înţelese aspectele
fundamentale ale limbajului, înainte ca ele să fie detaliate.
Toate aplicaţiile realizate prin intermediul limbajului C au anumite trăsături comune. Programele
conţin cel puţin o funcţie, fiecare incluzând una sau mai multe instrucţiuni. O funcţie poate fi definită ca
fiind o subrutină care poate fi apelată de diferite părţi ale unei aplicaţii. O instrucţiune se referă la o
acţiune care trebuie să fie executată de un program. Toate instrucţiunile scrise într-un program trebuie să
aparţină unei funcţii şi se termină obligatoriu cu caracterul ;.
Cea mai simplă formă a unei funcţii este următoarea:
nume_funcţie()
{
instrucţiuni;
}
unde nume_funcţie reprezintă numele unei funcţii, iar instrucţiuni reprezintă secvenţa de instrucţiuni (una
sau mai multe) care aparţine funcţiei declarate.
În ceea ce priveşte numele funcţiei (şi nu numai) trebuie spus că limbajul C este de tip CASE-
SENSITIVE, ceea ce presupune ca programatorul să aibă în vedere faptul că, compilatorul C face
deosebire între litere mari şi litere mici.
Orice program în limbajul C conţine cel puţin o funcţie: funcţia main(). Execuţia fiecărui program
începe cu funcţia main(), ceea ce înseamnă că dacă programatorul omite scrierea acestei funcţii,
compilatorul va fi în imposibilitatea compilării programului editat. Astfel, atunci când se execută un
program, primele instrucţiuni executate sunt cele care fac parte din funcţia main. Dacă programul conţine
mai multe funcţii, numele acestora va fi definit de utilizator.
Toate programele scrise în limbajul C sunt memorate într-un fişier sau mai multe, acestea având
extensia .c. Un fişier care conţine un program scris în C sau care conţine numai o parte a acestuia
se numeşte fişier sursă.
Prin compilarea fişierului sursă rezultă un fişier obiect, care are extensia .obj. Fişierele sursă
care intră în compunerea unui program pot fi compilate împreună sau separat. În urma unei compilării
rezultă un fişier obiect; fişierele obiect aferente aplicaţiei pot fi reunite într-un program executabil prin
link-editare. În urma link-editării rezultă un fişier executabil, cu extensia .exe.
Un nume este o succesiune de litere şi eventual cifre, primul caracter fiind literă. Pot fi utilizate
litere mici şi litere mari precum şi caracterul subliniere (_).
Există un număr de cuvinte împrumutate din limba engleză care au o semnificaţie predefinită.
Utilizatorul nu poate da o altă utilizare acestora şi de aceea se mai numesc cuvinte cheie. Acestea se scriu
mereu cu litere mici şi reprezintă nume cu destinaţii speciale (for, if, while, break, exit, etc.).
Atomi lexicali
Ca şi alte limbaje, C are un alfabet şi reguli pentru scrierea programelor corecte folosind semne
de punctuaţie. Aceste reguli formează sintaxa limbajului C. Compilatorul C are rolul de a testa dacă un
program editat este corect. Dacă sunt erori, atunci va afişa o listă de mesaje de eroare şi se va opri. Iniţial,
compilatorul împarte mulţimea caracterelor (programul sursă) în atomi lexicali, care reprezintă
vocabularul de bază al limbajului. În ANSI C (ANSI = American National Standards Institute) sunt şase
tipuri de atomi lexicali (care se mai numesc şi elemente lexicale sau unităţi lexicale):
cuvinte rezervate;
identificatori;
constante;
şiruri constante;
operatori;
semne de punctuaţie.
Caractere şi atomi lexicali
Un program C este o secvenţă de caractere; caracterele permise în programele C sunt:
literele mici şi literele mari ale alfabetului: a b ... z, A, B, …, Z
cifre : 0 1 ... 9
alte caractere: * / = ( ) { } [ ] < > ' " ! @ # $ % & _ | ^ ~ \ . , ; : ?
spaţii: blank, newline şi tab
Identificatori
Un identificator este un atom lexical compus dintr-o secvenţă de litere, cifre sau caracterul
underscore ("_") cu restricţia că primul caracter este o literă sau underscore. În multe implementări C, se
face distincţie între litere mici şi mari. În general, se obişnuieşte ca identificatorii să fie scrişi cu nume
sugestive care să uşureze citirea şi documentarea programului.
Exemple:
1. k, _id, contor, un_identificator sunt identificatori;
2. gresit#unu, 100_gresit_doi, -plus nu sunt identificatori.
Comentarii în C
Comentariile sunt şiruri de caractere cuprinse între /* şi */. Comentariile nu reprezintă atomi
lexicali. Practic, compilatorul va traduce comentariile într-un singur caracter spatiu, de aceea
comentariile nu fac parte din codul executabil.
Avantajele folosirii comentariilor:
uşurarea documentării ulterioare; scopul documentării este explicarea clară a folosirii
programelor;
un comentariu poate conţine informaţii care argumentează demonstraţia corectitudinii unui
algoritm;
comentariile sunt foarte bine venite a fi scrise odată cu textul programului.
Tipuri de date de bază
Mulţimea tipurilor de date predefinite constituie o caracteristică importantă şi un argument în
alegerea unui limbaj sau altul pentru rezolvarea unei probleme. În limbajul C există o serie de tipuri
simple de date predefinite (tabelul 2.1), a căror lungime poate să difere de la un calculator la altul şi de
la o implementare la alta.
Un tip de date descrie un set de date (care se mai numesc şi obiecte) care au aceeaşi reprezentare.
De asemenea, există un număr de operaţii asociat unui tip de date (spre exemplu, cu tipul întreg se
asociază cele patru operaţii aritmetice şi eventual şi altele).
Pentru fiecare tip predefinit din limbajul C există cuvinte rezervate, care reprezintă modificatori
de tip: unsigned, signed, long şi short pentru tipul int; signed şi unsigned pentru tipul char; long pentru
tipul double. Cuvintele rezervate dintre parantezele pătrate sunt opţionale şi diferitele combinaţii
definesc acelaşi tip de dată.
Tipurile de date specifice limbajului C sunt prezentate în tabelul 2.1.
De asemenea, o componentă importantă a unui program C o reprezintă biblioteca funcţiilor C (a
funcţiilor executate de un program) care pot realiza:
operaţii de intrare/ieşire,
operaţii matematice,
operaţii cu şiruri, etc.
Printre cele mai des folosite funcţii din C se numără funcţia printf, care este principala funcţie de
ieşire aferentă limbajului C, şi este redată sub următoarea formă:
printf(“sir de caractere”);
Pentru exemplul prezentat, pe ecran se va afişa şirul de caractere inclus între ghilimele.
Tabelul 2.1 Principalele tipuri de date în limbajul C
Tip Nr.octeţi Interval de valori Semnificaţie
int 4 [-2147483648 – 2147483647]
(-231 – 231-1)
conţine numere întregi cu semn,
reprezentate binar pe 4 octeţi
short int 2 [-32768 – 32767]
(-215 – 215-1)
conţine numere întregi cu semn,
reprezentate binar pe 2octeţi
long int 8 (-231 – 231-1) / (-215 – 215-1) conţine numere întregi cu semn,
reprezentate binar pe 8 octeţi
unsigned int 4 [0 –2147483647] (0-231 –1)
se foloseşte pentru a crea întregi fără
semn. Diferenţa dintre întreg cu semn şi
fără semn constă în modul în care
compilatorul interpretează bitul de semn.
Dacă bitul de semn este 0, numărul este
pozitiv, şi dacă bitul de semn este 1,
numărul este negativ. De cele mai multe
ori, numerele negative se reprezintă în
complement faţă de doi. Dacă un întreg
este declarat cu unsigned, când bitul de
semn este 1, numărul devine 231-1
[signed]
char 1 [-128 – 127] (-27 – 27-1)
sunt caractere, adică simboluri
tipografice elementare: litere, cifre,
semne de punctuaţie, simboluri
matematice, etc;
unsigned
char 1 [0 – 255] (28-1) caracter fără semn
Float 4 [3,4*10-38 – 3,4*1038]
zecimal în virgulă flotantă simplă
precizie – conţin numere reprezentate în
virgulă flotantă (pot avea partea
fracţionară)
double 8 [1,7*10-308 – 1,7*10308]
zecimal în virgulă flotantă dublă precizie
– conţin numere reprezentate în virgulă
flotantă (pot avea partea fracţionară)
long double 12 [3,4*10-4932 – 1,1*104932] Reprezentare flotantă în dublă precizie
Fiecare program scris în limbajul C poate fi prelucrat înaintea procesului de compilare prin
intermediul preprocesării, care este un proces automat realizat de compilator înaintea compilării
programului sursă. Rolul preprocesării este de a include diferite fişiere cu texte sursă, definiţii şi apeluri
de macrouri sau de a realiza compilarea condiţionată.
Astfel, o altă componentă importantă care caracterizează majoritatea programelor C sunt fişierele
header. Toate informaţiile aferente funcţiilor din biblioteca standard se află în fişiere care sunt transmise
împreună cu compilatorul, aceste fişiere recunoscându-se prin intermediul extensiei: .h. Informaţiile din
aceste fişiere sunt folosite de compilator pentru lucrul cu funcţiile din bibliotecă. Toate fişierele necesare
se adaugă prin intermediul directivei preprocesor #include, care are rolul de a determina compilatorul
să citească un alt fişier şi să-l includă în program.
Preprocesarea presupune prelucrarea informaţiilor specifice care sunt precedate de caracterul diez
(#). Astfel, un fişier cu text sursă poate fi inclus în cadrul programului curent prin intermediul construcţiei
#include, folosind unul din formatele:
Un fişier cu text sursă poate fi inclus cu ajutorul construcţiei #include. Această construcţie are
formatul:
#include “specificator_fişier” sau #include<specificator_fişier>
unde specificator_fişier (care depinde de sistemul de operare) defineşte un fişier cu text sursă păstrat pe
disc. În faza de preprocesare, textul fişierului respectiv se substituie construcţiei #include. Astfel, textul
fişierului respectiv ia parte la compilare împreună cu textul în care a fost inclus.
În cazul sistemului de operare DOS, specificatorul de fişier trebuie să fie un nume de fişier
împreună cu extensia lui (.C pentru compilatorul C, .H pentru fişiere de tip header – fişiere cu
prototipuri, etc.). De asemenea, în afară de numele şi extensia fişierului, specificatorul de fişier poate
conţine şi o “cale” dacă este necesar, pentru localizarea fişierului.
Diferenţa dintre cele două formate constă în modul de căutare al fişierului de inclus:
formatul cu paranteze unghiulare <…> se foloseşte la includerea fişierelor standard, cum sunt cele
care conţin prototipuri pentru funcţii din bibliotecă;
în cazul în care se foloseşte formatul cu ghilimele, fişierul este căutat în directorul curent sau conform
“căii” dacă aceasta este prezentă.
Spre exemplu, pentru a include fişierul sursă stdio.h care conţine prototipul funcţiilor de
intrare/ieşire se va folosi construcţia: #include <stdio.h>. Dacă se foloseşte construcţia #include
„un_fisier.c”, atunci compilatorul va include fişierul un_fisier din cadrul directorului curent. De
asemenea, pentru a include în program fişierul un_fisier care se găseşte în folderul Aplicaţii, în directorul
C, se va folosi construcţia: #include „c:\\Aplicatii\\un_fisier.c”. În cadrul ultimului exemplu prezentat,
se obervă că pentru a descrie calea, caracterul backslash se dublează în concordanţă cu convenţia de
reprezentare a caracterului backslash într-un şir de caractare.
Un fişier standard care se include frecvent este stdio.h; acesta conţine prototipurile pentru o serie
de funcţii ce realizează operaţii de intrare/ieşire.
Construcţiile #include se scriu de obicei la începutul fişierelor sursă, pentru ca textele inserate să
fie valabile în tot fişierul sursă care se compilează.
Pentru a putea folosi într-un program funcţia printf(), este necesară includerea directivei
#include<stdio.h> deoarece, aşa cum spuneam anterior, această directivă conţine informaţii cu privire la
această funcţie de ieşire. Directivele incluse în program nu se finalizează cu caracterul ;, deoarece nu
reprezintă un cuvânt cheie al programului, ci o “instrucţiune” către compilatorul C.
Cel mai simplu program scris în limbajul C este următorul:
Exemplu:
#include <stdio.h>
main()
{
printf("Primul program in limbajul C !!!");
getch();
}
care va afişa pe suprafaţa ecranului mesajul: Primul program in limbajul C !!!.
Observaţie: getch() este o funcţie de ieşire fără ecou (caracterul tipărit nu este afişat pe ecran) asupra
căreia se va reveni în cursul aferent funcţiilor de intrare/ieşire; este folosită de mediul Dev-C++ pentru
a păstra pe ecran (până la apăsarea unei taste) rezultatul programului compilat.
Practic, în acest program banal sunt incluse toate aspectele comune şi obligatorii aferente unui
program scris în C. Prima linie permite includerea în program a fişierului stdio.h. pentru a fi citit de
compilator. Începutul funcţiei principale este desemnat de a doua linie, main(). Corpul tuturor funcţiilor
în C (deci, şi a funcţiei main) este inclus între două paranteze acoladă; tot ce este scris în interiorul acestor
paranteze acolade constituie corpul funcţiei. În cazul programului prezentat anterior, corpul funcţiei
main() este format dintr-o singură instrucţiune, care apelează funcţia printf() din biblioteca standard
pentru a permite afişarea pe ecran a şirului dorit. Atunci când corpul unei funcţii este format din mai
multe instrucţiuni, parcurgerea acestora se va face secvenţial.
Constante şi variabile în limbajul C
O constantă în limbajul C are un tip şi o valoarea. Atât tipul cât şi valoarea sunt determinate de
caracterele care intră în compunerea constantei. Valoarea unei constante nu poate fi schimbată în timpul
execuţiei programului în care a fost utilizată. Există mai multe tipuri de constante:
întregi – sunt constante care pot fi scrise în sistemul de numeraţie în baza 8, 10 sau 16. O constantă
zecimală întreagă este un şir de cifre zecimale care are prima cifră diferită de zero. Constantele octale
reprezintă succesiuni de cifre octale (0-7) precedate de un zero nesemnificativ. O constantă hexazecimală
este o succesiune de cifre hexazecimale precedate de 0x sau 0X.
Ex: 15150 care se reprezintă binar prin: 00000000 00000000 00111011 00101110
flotante – reprezintă un număr raţional şi se compune din:
o parte întreagă care poate fi şi vidă – este o constantă zecimală;
o parte fracţionară care poate fi şi vidă – se compune din caracterul punct după care urmează o
succesiune de cifre zecimale;
un exponent care poate fi şi vid – începe cu litera e sau E, după care urmează un semn opţional
(plus sau minus) şi un şir de cifre zecimale. Exponentul defineşte un factor care exprimă o putere
a lui 10.
Ex:
100. – 100
100.48 – 100,48
.48 – 0,48
12e5 – 12*105
.5E-5 – 0,5*10-5
15.255e2 – 15,255*102
1500.123e-3 – 1500,123*10-3
caracter – o constantă caracter are ca valoare codul ASCII al caracterului pe care-l reprezintă şi are
tipul int. O constantă caracter corespunzătoare unui caracter imprimabil se reprezintă prin caracterul
respectiv inclus între caractere apostrof:
‘A’ – valoarea 65
‘a’ – valoarea 97
‘*’ – valoarea 42
Recommended