Sunteți pe pagina 1din 10

Elemente de baza ale limbajului C Scurt istoric al limbajului. Caracteristici generale.

Limbajul C a fost conceput in laboratoarele Bell in anii 70 de catre Dennis Ritchie pentru sistemul de operare Unix. Atat sistemul de operare
Unix, cat si unele utilitare din Unix sunt scrise in limbajul C. Dennis Ritchie a scris primul compilator de C pe un PDP-11, urmand ca in curand acesta
sa se raspandeasca cu rapiditate si pe alte tipuri de calculatoare. Desi din punct de vedere istoric limbajul C este strans legat de sistemul de operare
Unix, C-ul nu depinde de vreun anumit sistem de operare sau de vreo masina anume. Este numit uneori limbaj de sisteme de operare deoarece
poate fi folosit cu succes in scrierea sistemelor de operare, dar se poate utiliza la fel de bine si in probleme matematice, in scrierea editoarelor de
texte sau la bazele de date.

Limbajul C este un limbaj cu caracter general, caracterizat de compactitate, un set puternic de operatori si portabilitate. Nu poate fi numit
limbaj de programare de nivel inalt, si nici nu este creat pentru un anumit domeniu. Totusi, lipsa restrictiilor si caracterul sau general il fac mult mai
eficient decat multe alte limbaje de programare de nivel inalt. Limbajul C nu contine operatii asupra obiectelor compuse, cum ar fi sirurile de
caractere, multimile, listele sau blocurile. Nu contine nici macar functii de intrare/iesire; acestea sunt incluse in biblioteci din afara limbajului.

Multe dintre ideile limbajului C provin de la mai vechuil limbaj BCPL, dezvoltat de Martin Richards. Influenta directa s-a realizat prin
intermediul limbajului B scris de Ken Thompson in 1970 pentru un PDP-7. Desi au cateva caracteristici comune, C-ul nu poate fi in nici un caz numit
o varianta a BCPL-ului.

Primii pasi in C
Vom incepe studiul limbajului C prin asimilarea rapida a notiunilor de baza. Scopul este acela de a scrie programe
functionale fara insa a acorda atentie regulilor formale, detaliilor neesentiale sau exceptiilor. Nu urmarim deci
exhaustivitatea, ba nici chiar exactitatea; trebuie sa se ajunga cat de poate de repede la punctul in care sa se poata scrie
deja programe functionale. Din acest motiv, ne vom concentra pentru inceput atentia asupra cunostintelor de baza:
variabile, constante, aritemetica, instructiuni, intrari si iesiri.

Singura metoda pentru a invata un nou limbaj de programare este scrierea de programe in acest limbaj. Limbajul C
nu are proceduri, ci numai functii; un program C este alcatuit, indiferent de dimensiunea sa, dintr-una sau mai multe functii,
dintre care una este principala si poarta numele de main (engl. main = principal, important) . In concluzie, fiecare program
contine cel putin o functie, si anume functia main. Iata cum arata cel mai simplu program C (care, evident, nu realizeaza
nimic): void main()

Numele functiilor este in general arbitrar, insa main este un nume special - executia programului incepe intotdeauna cu
prima instructiune a functiei main. Rezulta de aici ca in orice program C trebuie sa apara pe undeva functia main. Pentru
indeplinirea scopului problemei, functia main va apela in general alte functii, dintre care unele sunt definite in acelasi
program, iar altele provin din anumite biblioteci predefinite.

Observam faptul ca dupa cuvantul main urmeaza o pereche de paranteze rotunde. Asa cum este normal, o functie
are parametri formali. In cazul de fata lista parametrilor formali este vida, iar perechea de paranteze indica acest fapt. Spre
deosebire de limbajul Pascal, in care tipul unei variabile sau functii se preciza dupa numele ei, in C decalrarea unei variabile
sau functii incepe chiar cu tipul acesteia.

Functia main neintorcand nici o valoare, va fi de tip void (engl. void = vid).

Perechea de acolade cuprinde instructiunile din care este formata functia. Ele corespund perechii begin end din
Pascal. In cazul de fata, functia nu contine nici o instructiune si perechea de acolade este vida.

Programul de mai jos tipareste clasicul Hello, world!.

#include <stdio.h>

void main()

Asa cum am precizat deja, limbajul C nu contine instructiuni de citire/scriere. Linia: printf(Hello, world!n) ;este
un apel de functie, in care functia printf se apeleaza cu argumentul Hello, world!. printf este o functie de biblioteca al
carei rezultat este scrierea textului pe ecran (in lipsa specificarii unui alt terminal). Pentru a putea utiliza
functia printf compilatorul trebuie sa stie prototipul ei. Prototipul functiei printf se afla in fisierul antet (header) stdio.h (de la
STanDard Input Output Header) alaturi de prototipurile altor functii de citire/scriere pe care le vom invata in curand; deci
directiva de precompilare

#include <stdio.h> precizeaza fisierul in care se afla aceste prototipuri.

Caracterul n care apare la sfarsitul sirului de caractere afisat reprezinta modul prin care se indica in C trecerea la
linie noua (are acelasi efect ca si writeln-ul din Pascal). Daca omitem n-ul (merita incercat) vom constata ca dupa rularea
programului cursorul ramane la sfarsitul sirului afisat, deci nu se mai realizeaza trecerea la linie noua.

Functia printf nu realizeaza niciodata trecerea la linie noua, deci putem afisa caractere pe o singura linie folosindu-
ne de apeluri multiple ale ei. Secventa de program urmatoare afiseaza sirul identic cu programul anterior:

printf(Hello, );

printf(world!);

printf(n);

Mai trebuie sa observam faptul ca n reprezinta un singur caracter. Asemanatoare cu n-ul sunt si
alte caractere escape; ele reprezinta un mecanism general utilizat pentru reprezentarea caracterelor netiparibile. In limbajul
C mai intalnim t pentru tabulare, b pentru backspace, pentru ghilimele, iar reprezinta chiar caracterul backslash.

Exercitiul I.2. Incercati ce se petrece daca in sirul de caractere afisat de printf introduceti caracterul x, unde x este un
caracter ce nu figureaza in lista de mai sus.

Variabile si aritmetica
Programul de mai jos tipareste urmatorul tabel de conversie de la grade Fahrenheit la grade Celsius conform
urmatoarei formule: C = (5/9)(F-32).

0 -17.8
20 -6.7
40 4,4
60 15,6

300 148,9
Iata si programul:

/* Program de conversie a gradelor Fahrenheit in grade Celsius

pentru f = 0, 20, , 300 */

void main()

Primele doua linii:

/* Program de conversie a gradelor Fahrenheit in grade Celsius


pentru f = 0, 20, , 300 */

reprezinta un comentariu care in cazul nostru informeaza pe scurt despre ceea ce realizeaza preogramul. Compilatorul C
nu ia in seama caracterele cuprinse intre /* si */; putem deci cuprinde intre ele diferite texte care pot fi de folos in intelegerea
programului. Comentariile pot aparea oriunde poate aparea un spatiu sau o linie noua.

In limbajul C (la fel ca in Pascal) toate variabilele trebuie declarate inainte de a fi utilizate; declararea se realizeza de
obicei la inceputul functiei, inainte de orice instructiune. Daca uitam aceasta la compilare vom primi mesaje de eroare. O
declaratie consta in specificarea unui tip si enumerarea in continuare a variabilelor de acel tip. In acest sens, putem lua ca
exemplu doua linii ale programului:

int liminf, limsup, pas ;

float fahr, celsius ;

Cuvantul rezervat int indica faptul ca variabilele care-i urmeaza sunt de tip intreg (integer) si este echivalent cu integer din
Pascal. float reprezinta variabilele in virgula mobila (floating point), adica acele numere care au si parte fractionara si este
echivalent cu real din Pascal. Precizia tipurilor int, si float depinde de masina pe care lucram. Pentru calculatoarele
compatibile IBM-PC precizia este de 16 biti (de la -32768 la 32767) pentru tipul int, respectiv 32 biti (3.4 * 10 -38 pana la 3.4 *
1038) pentru tipul float.

Pe langa int si float in C mai exista si alte tipuri de baza:

char - caracter, un singur octet

short - intreg scurt

long - intreg lung

double - virgula mobila in dubla precizie

Dimensiunea obiectelor de acest tip depinde de asemeni de masina, le vom discuta in detaliu mai tarziu.

In programul de nostru de conversie calculul efectiv incepe odata cu instructiunile de initializare:

liminf = 0 ;

limsup = 300 ;

pas = 20 ;

Se observa ca unele instructiuni sunt despartite de ;.

Fiecare linie a tabelului se calculeaza in mod identic, de aceea vom folosi un ciclu care se a repeta pentru fiecare
linie a tabelului; acesta este rolul instructiunii while:

while(fahr <= limsup)

In timpul rularii, masina verifica daca se respecta conditia dintre paranteze. Daca valoarea ei este adevarata (fahr este mai
mic sau egal cu limsup) atunci va executa instructiunile dintre acolade (numite si corpul ciclului). Apoi verifica din nou
conditia, iar daca valoarea ei este adevarata va executa din nou corpul ciclului. Daca in urma verificarii conditiei se obtine o
valoare logica falsa (fahr devine mai mare decat limsup), ciclul se incheie iar executia se continua cu urmatoare instructiune
dupa ciclu. Programul de mai sus nu mai contine alte instructiuni, deci executia lui se incheie.

In corpul while-ului pot aparea mai multe instructiuni intre acolade sau o singura instructiune fara acolade, ca in
exemplul de mai jos:

while(i<j)

i = 2*i ;

In cazul in care ciclul nu contine nici o instructiune (pare paradoxal, dar vom intalni multe exemple in acest sens) se pune ;
dupa while.

Constante simbolice
se inlocuieste 0,20 si 300 cu directiva #define

modificarea programului a.i. sa fie mai scurt

posibilitatea initializarii la declarare

Cateva programe utile


In cele ce urmeaza vom prezenta cateva programe legate unul de altul care realizeaza operatii simple asupra unor date de
tip caracter. Vom observa ca multe programe sunt doar o varianta mai complexa a prototipurilor prezentate aici.
Citirea si scrierea caracterelor
In biblioteca de intrare iesire standard ni se pun la dispozitie functii cu care se poate scrie sau citi un singur caracter la un
moment dat. Functia getchar() citeste la fiecare apel al sau urmatorul caracter de la intrare si va returna acest caracter.
Deci dupa un apel de forma:
c = getchar()
variabila c va contine urmatorul caracter de la intrare. In general caracterele sunt citite de la terminal (tastatura), dar despre
acest lucru vom discuta mai tarziu.

Opusul functiei getchar() este functia putchar. Un apel de forma putchar(c) are ca efect scrierea continutului variabilei c
la un periferic de iesire care este din nou un terminal (monitorul).

Copiere de caractere
Flosindu-ne de fucntiile getchar si putchar putem scrie surprinzator de multe programe utile, fara a sti nimic altceva
despre citire si scrirere. Cel mai simplu exemplu in acest sens este un program care copiaza intrarea la iesire caracter cu
caracter. Schita programului este:

Citeste un caracter

Cat_timp(caracterul citit nu indica sfarsitul intrarii)

Scrie caracterul citit la iesire

Citeste un nou caracter

Exprimat in C acest lucru devine:

#include <stdio.h>
void main()

Simbolul != inseamna in C diferit.

Principala problema este sesizarea sfarsitului intrarii. S-a convenit ca atunci cand functia getchar() ajunge la
sfarsitul intrarii (marcata de ^Z) sa intoarca o valoare ce nu reprezinta codul nici unui caracter; astfel vom putea sesiza in
program momentul in care toate caracterele au fost citite. Problema oarecum neplacuta este ca exista doua tipuri de
conventii uzuale referitoare la valoarea returnata la sfarsitul intrarii. Am evitat aceasta problema folosind in loc de o valoare
numerica o constanta predefinita: EOF. In practica, EOF este 0 sau -1, functie de compilator. Variabila c a fost
declarata int si nu char pentru a putea retine in ea si valoarea EOF returnata la sfarsitul intrarii.

Un programator C experimentat, ar scrie mai compact programul de mai sus. In limbajul C atribuirile de genul

c = getchar()

se pot folosi si in expresii; valoarea expresiei de mai sus este pur si simplu valoarea atribuita membrului din dreapta.
Introducand atribuirea referitoare la caracterul c in capul ciclului while, programul se rescrie astfel:

#include <stdio.h> /*copierea intrarii la iesire, var. a doua*/

void main()

explicarea programului

parantezele din while sunt absolut necesare; precedenta lui != este mai mare decat a lui =

Numarare de caractere
Programul urmator, obtinut prin modificarea programului de copiere, numara caracterele citite de la intrare:

#include <stdio.h> /*numarare de caractere*/

void main()

Comentarii:

operatorul ++; exista si -- ; poate fi prefix si postfix

am folosit long pentru a retine mai multe caractere; se tipareste cu %ld

Se poate folosi si double pentru o precizie mai buna. Vom folosi for in loc de while pentru a exemplifica un alt mod de
organizare a ciclului:

#include <stdio.h> /*numarare de caractere*/

void main()

Comentarii:

%0.f pentru a elimina zecimalele inexistente


corpul ciclului for este gol; scrierea lui ; pe o linie noua pentru a fi remarcat.

verificarea conditiei se face in capul ciclului, deci programul functioneaza si pentru intrare vida

Numarare de linii
Urmatorul program numara liniile introduse la intrare. Presupunem ca fiecare linie se incheie cu n.

#include <stdio.h> /*numarare de linii*/

void main()

if-ul

== pentru comparare. Atribuirile apar de doua ori mai des.

orice caracter cuprins intre se numeste constanta caracter si reprezinta codul sau numeric (ASCII la noi). De exemplu,
codul numeric al lui A este 65 in ASCII. Evident, este mai comod sa scriem A in loc de 65, programul fiind si mai portabil.
Se permite ca o constanta caracter sa contina si un caracter escape ( de exemplu n). Sa nu uitam ca nreprezinta un
singur caracter.

Exercitiul I.2 Scrieti un program care numara spatiile, tab-urile si newline-urile de la intrare.

Exercitiul I.3. Scrieti un program care copiaza intrarea la iesire, inlocuind sirurile de caracrere formate din unul sau mai
multe spatii cu un singur spatiu.

Blocuri
Sa scriem un program care numara de cate ori apare fiecare cifra, de cate ori apar delimitatorii (spatiu, t sau n) si cate
caractere de alt tip apar la intrare! Acest exercitiu pare putin cam artificial, dar ne ofera posibilitatea de a studia cateva
caracteristici ale limbajului C intr-un singur program.

Trebuie sa deosebim douasprezece tipuri de caractere, deci merita sa stocam numarul de aparitii al diverselor cifre
intr-un vector (bloc) decat sa lucram cu douasprezece variabile diferite. Iata una dintre variantele posibile ale programului:

void main()

Declaratia

int ndigit[10]

exprima faptul ca ndigit este un bloc format din 10 numere intregi. Indexul unui bloc incepe in C intotdeauna cu 0 (nu ca in
Fortran, sau cum se obisnieste in Pascal cu 1), astfel elementele blocului vor fi ndigit[0], ndigit[1], , ndigit[9]. Acest lucru
este evidentiat si in ciclurile for: unul initializeaza, iar celalalt afiseaza sirul.

Indexul este o expresie oarecare de tip intreg.

Programul de fata se foloseste din plin de modul in care sunt reprezentate intern caracterele. De exemplu conditia

if( c>=0 && c<=9 )

decide daca variabila c contine o cifra sau nu. Daca este, atunci valoarea ei numerica este:
c-0.

Aceasta metoda poate fi utilizata doar daca codurile caracterelor 0, 1, , 9 sunt numere crescatoare si intre 0 si 9
sunt reprezentate numai cifre (codul ASCII respecta aceasta conditie: 0 = 48, iar 9 = 57).

In expresiile ce contin tipurile char sau int, confrom definitiei, inainte de evaluare se convertesc toate valorile la int,
deci constantele sau variabilele de tipul char sunt in esenta echivalente cu tipul int. Aceasta rezolvare este pe cat de
naturala, pe atat de eficienta: de exemplu c-0 este o expresie de tip intreg a carei valoare este cuprinsa intre 0 si 9,
conform cu indexul cerut pentru ndigit.

Conditiile de genul

if(conditie)

instructiune

else if(conditie)

instructiune

else

instructiune

sunt deseori folosite pentru ramificarile multiple.

atentie la else care este legat de ultimul if

vom vorbi de switch

Exercitiul I.4 Scrieti un program care afiseaza histograma lungimii cuvintelor date la intrare. (orizontal).

Functii C
O functie C este echivalentul subrutinei sau functiei din Fortran sau a procedurilor si functiilor din Pascal. Functia
este o modalitate comoda de a incapsula anumite parti ale programului in cutii negre pe care le vom putea apoi utiliza fara
sa ne pese de continutul ei.

alte detalii

Pana acum nu am folosit decat functii pe care le-am avut la dispozitie gata-facute, cum ar fi putchar sau pritnf; a
sosit timpul sa construim si noi propriile noastre functii. Deoarece in C nu exista un operator de ridicare la putere ca ** din
Fortran, vom studia tehnica definirii functiilor sciind o functie power(x,n) ce ridica numarul intreg x la puterea intreaga n.
Deci power(2,5) va fi 32.

Iata cum se defineste si se utilizeaza functia power:

int power(int x, int n)

void main()

Orice functie are forma:


tip_functie nume_functie( lista optionala de parametri )

Functiile pot aparea in orice ordine si pot aparea si in fisiere sursa diferite.

modul de apelare

declararea parametrilor formali.

Exercitiul I.5 Sa se scrie un program C care transforma textul citit in litere mici utilizand pentru aceasta o functie int
lower(int c) ce intoarce valoarea c daca c nu este litera mare si corespondentul mic al lui c daca e litera mare.

Argumente; transmitere prin valoare


O caracteristica a limbajului C considerata -mai ales de programatorii Fortran- neobisnuita: transmiterea
parametrilor se face intotdeauna prin valoare. Aceasta inseamna ca functia apelata nu primeste adresa la care se afla
parametrii actuali ci o copie (valoarea) a lor in variabile temporare(de fapt, intr-o stiva).

De aici rezulta diferenta principala, si anume ca functia nu poate modifica valoarea parametrilor actuali, deoarece ea
lucreaza cu o copie a lor, originalul ramanand neschimbat.

Acesta insa nu este un balast, ci un avantaj. Se pot scrie astfel programe mai compacte, folosind mai putine
variabile auxiliare, deoarece putem utiliza parametrii in acelasi mod ca variabile initializate clasic. Iata o varianta a functiei
power, care foloseste acest fapt:

int power(int x, int n)

Am folosit aici parametrul m ca pe o variabila auxiliara pe care am decrementat-o pana cand a ajuns la valoarea 0;
variabila i devine asrfel inutila. Orice operatie pe care o efectuam asupra variabilei n, nu are nici o repercusiune in
programul principal.

In caz de nevoie se poate face in asa fel incat functia sa poata modifica valoarea variabilei din functia care o
apeleaza. Functia apelanta va trebui sa furnize adresa variabilei ce se doreste a fi modificata, iar functia apelata trebuie sa
declare parametrul respectiv va fiind un pointer. Variabila efectiva va fi accesata astfel indirect, prin intermediul acestui
pointer. (Aceasta problema se rezolva mult mai elegant in C++). ne vom ocupa de aceasta mai tarziu.

Daca folsim ca parametru numele unui bloc, atunci valoarea care este transmisa functiei este de fapt chiar adresa
primului element al blocului. Indexand aceasta valoare se poate accesa oricare element al blocului (elementele unui vector
nu sunt copiate).

Siruri de caractere
Cel mai des intalnit sir in C este, probabil, sirul de caractere. Vom studia modul in care se folosesc sirurile de
caractere si functiile aferente lor cu ajutorul unui program care citeste linii si o afiseaza pe cea mai lunga dintre ele.
Structura de baza este foarte simpla:

cat timp(mai exista linii)

daca(e mai lunga decat linia maxima curenta)

stocheaza linia si lungimea ei

tipareste cea mai lunga linie


Aceasta structura pune in evidenta modularea naturala a programului. Prima parte citeste si verifica linia noua, a doua o
stocheaza si a treia conduce intregul proces.

Deoarece sarcinile se pot separa asa de natural, este normal sa scriem programul tinand cont de aceasta separare.
Deci vom scrie mai intai o functie getline care citeste de la intrare urmatoarea linie; ea este o generalizare a
functiei getchar. Functia getline va returna lungimea liniei sau 0 in cazul in care s-a ajuns la sfarsitul intrarii.(chiar si o linie
ce contine doar o trecere la linie noua, are lungimea 1).

Daca vom constata ca linia curenta este mai lunga decat cea mai lunga linie de pana acum, ea va trebui undeva
stocata. Apare deci naturala necesitatea unei alte functii numita copy care va copia linia noua intr-un loc sigur.

Iata acum programul:

#define MAXLINE 1000

int getline(char s[])

void copy( char dest[], char source[])

void main()

if( max>0 ) /*au fost linii*/

printf(%s, mline) ;

Comentarii:

cum se transmit parametrii de tip sir

se pune 0 la sfarsit

valoare se transmite prin return.

Exercitiul I.6 Sa se scrie un program care tipareste toate liniile mai lungi de 80 de caractere!

Exercitiul I.7 Sa se scrie un program care elimina spatiile sau tab-urile de la sfarsitul liniilor si sterge liinile complet goale!

Exercitiul I.8 Sa se scrie o functie reverse(s) care rastoarna sirul de caractere s! Se va folosi apoi aceasta functie pentru un program care
inverseaza intrarea linie cu linie.

Rezumatul capitolului
Am parcurs in acest capitol cele mai importante elemente ale limbajului C. Chiar si cu aceste cateva elemente de baza putem deja construi
programe utile de dimensiuni impunatoare. Pentru aprofundarea elementelor prezentate pana aici va propunem spre rezolvare cateva probleme de
un nivel ceva mai ridicat decat cele de pana acum:

Exercitiul I.9 Sa se scrie un program care pliaza liniile intrarii in jurul primului caracter diferit de spatiu aflat inainte de pozitia a n-a (n este
parametru!)! Verificati ca programul functioneaza corect atunci cand sirul de la intrare este foarte lung, dar si atunci cand inainte de pozitia data nu
apare nici un delimitator!

Exercitiul I.10 Sa se scrie un program care elimina dintr-un program C toate comentariile! Sa nu uitam de tratarea corecta a sirurilor de caractere
cuprinse intre ghilimele!
Exercitiul I.11 Sa se scrie un program care verfica intr-un program C greselile elementare de sintaxa, cum ar fi numarul parantezelor rotunde patrate
sau al acoladelor deschise sa fie egal cu cele inchise! Sa nu uitam de apostrofuri, ghilimele si nici de comentarii!

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