CATEGORII DOCUMENTE |
Limbajul C a fost conceput in laboratoarele
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.
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.
Programul de mai jos tipareste urmatorul tabel de conversie de la grade Fahrenheit la grade Celsius conform urmatoarei formule:
C = (5/9)(F-32).
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.
se inlocuieste 0,20 si 300 cu directiva #define
modificarea programului a.i. sa fie mai scurt
posibilitatea initializarii la declarare
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.
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).
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 =
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
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 'n' reprezinta 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.
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).
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.
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).
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 separe 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.
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!
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 1403
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved