|
Politica de confidentialitate |
|
• domnisoara hus • legume • istoria unui galban • metanol • recapitulare • profitul • caract • comentariu liric • radiolocatia • praslea cel voinic si merele da aur | |
INITIERE IN C | ||||||
|
||||||
Sa incepem cu o introducere rapida in C. Scopul nostru este sa preezentam elementele esentiale ale limbajului in programe reale, fara insa a ne impotmoli in detalii, reguli formale si exceptii. In acest punct al expunerii nu incercam sa fim completi si nici macar foarte precisi (mentionam totusi ca exemplele vor sa fie corecte). Dorim sa va aducem cit mai repede posibil in punctul in care veti fi capabili sa scrieti programe utile si, pentru aceasta, ne-am concentrat asupra fundamentelor: variabile si constante, aritmetica, controlul fluxului, functii si rudi mente de operatii de I/O. Am lasat deoparte intentionat din acest capitol acele caracteristici ale limbajului C care sint de importanta vitala in scrierea programelor mai mari. Acestea includ pointerii, structurile, majoritatea din bogatul set de operatori ai lui C, anumite instructiuni de control al fluxului si o multime de detalii. v8d9dn Acest mod de abordare are neajunsurile lui, desigur. Cel mai notabil este acela ca povestea completa a caracteristici lor oricarui limbaj de programare nu este gasita intr-un singur loc, iar o initiere in el, fiind scurta, poate induce in eroare. Deoarece exemplele nu pot folosi intreaga putere a lui C, ele nu sint atit de concise si de elegante pe cit ar putea fi. Am incercat sa minimalizam aceste efecte, dar fiti atenti! Un alt neajuns este acela ca in capitolele urmatoare vom repeta in mod necesar cite ceva din acest capitol. Speram ca aceasta repetitie va va ajuta mai mult decit va va plictisi. In orice caz, programatorii experimentati vor fi capabili sa extrapoleze din materialul din acest capitol propriile lor nevoi de programare. Incepatorii vor putea scrie mici programe, similare celor prezentate de noi. Ambele grupe pot folosi acest capitol drept cadru pentru descrierile riguroase care incep cu Capitolul 2. 1.1 Sa incepem Singurul mod de a invata un nou limbaj de programare este de a scrie programe in el. Primul program pe care-l vom scrie este acelasi pentru toate limbajele: Tipariti cuvintele hello, world Acesta este primul obstacol; pentru a sari peste el, trebuie sa fiti in stare sa creati undeva textul program, sa-l compilati cu succes, sa-l incarcati, sa-l executati si sa aflati textul tiparit acolo unde este iesirea calculatorului dumneavoastra. In C, programul pentru a tipari "hello, world" este: main () Cum ruleaza acest program, depinde de sistemul pe care-l folositi, Drept exemplu specific, pe sistemul de operare RSX, trebuie sa creati acest program sursa intr-un fisier al carui nume se termina in ".C", de exemplu "hello.C" apoi sa-l compilati cu comenzile: >cc hello Daca n-ati gresit nimic, de exemplu sa fi uitat un caracter sau sa fi inversat doua caractere, compilarea se va desfasura silentios si va produce un fisier obiect numit "hello.obj". Lansindu-l in executie dupa linkeditare cu comenzile >tkb hello=hello,lb:a1,1iclib/lb va produce hello, world ca iesire a sa. Pe alte sisteme, regulile vor fi diferite; verificati-le cu expertul local. Exercitiul 1.1. Executati acest program pe sistemul dumneavoas tra. Incercati sa vedeti ce mesaje de eroare obtineti, lasind la o parte parti din program. Si acum citeva explicatii despre programul insusi. Un program C, oricare i-ar fi marimea, consta din una sau mai multe "functii" care specifica operatiile efective de calculat care trebuiesc facute. Functiile din C sint similare cu functiile si subrutinele dintr-un program Fortran sau cu procedurile din PL/1, Pascal,etc. O metoda de a comunica date intre functii este prin argumen tele functiilor. Parantezele care urmeaza dupa numele functiei includ lista de argumente. In cazul nostru, "main" este o functie
fara argumente ceea ce se indica prin "()". Acoladele "A S"
includ instructiunile care alcatuiesc functia. Ele sint ana loage lui "DO-END" din PL/1 sau lui "begin-end" din ALGOL,
Linia care spune: printf("hello, world\n"); este un apel de functie, care cheama o functie numita "printf" cu argumentul ("hello, world\n"). "printf" este o functie din biblioteca care tipareste pe terminal (daca nu este specifica ta o alta destinatie). In acest caz, ea tipareste sirul de caractere care alcatuiesc argumentul. O secventa alcatuita din orice numar de caractere cuprinse intre doua ghilimele "..." se numeste sir de caractere sau constanta sir. Pentru moment, singura folosire a sirurilor de caractere va fi ca argumentele pentru "printf" si alte functii. Secventa "\n" din sir este notatia din C pentru caracterul "linie
noua", care, cind este tiparit, avanseaza cursorul terminalu lui la marginea din stinga a urmatoarei linii. Daca uitati printf("hello, world compilatorul C va va tipari un diagnostic neprietenos despre ghilimele absente. "printf" nu furnizeaza o linie noua in mod automat, asa ca apelurile multiple pot fi folosite pentru a tipari o linie pe etape. Primul nostru program poate fi scris la fel de bine si astfel: main() pentru a produce o iesire identica. Exercitiul 1.2. Experimentati sa vedeti ce se intimpla cind sirul argument din "printf" contine "\x" un x este un caracter
oarecare care nu a fost listat mai sus. 0 -17.8 Iata acum si programul: /* Print Fahrenheit-Celsius table for f = 0, 20, ..., 300 */ main() Primele doua linii : /* Print Fahrenheit-Celsius table for f = 0, 20, ..., 300 */ sint un comentariu, care in acest caz explica pe scurt ce face programul. Orice caractere cuprinse intre "/*" si "*/" sint ignorate de compilator; ele pot fi folosite liber pentru a face programul mai usor de inteles. Comentariile pot apare oriunde poate aparea un spatiu sau o linie noua. In limbajul C, toate variabilele trebuie declarate inainte de a fi folosite, deobicei la inceputul liniei, inaintea oricarei instruc tiuni executabile. Daca veti uita o declaratie, veti primi un diagnostic de la compilator. O declaratie consta dintr-un "tip" si o lista de variabile care au acel tip, ca in: int lower, upper, step; float fahr, celsius; Tipul "int" implica faptul ca variabilele listate sint intregi; "char" caracter - un singur octet Marimea acestor obiecte este deasemenea dependenta de calculator; detalii se dau in Capitolul 2. Exista deasemenea "tablouri", Calculul efectiv in programul de conversie temperatura incepe cu asignarile: lower = 0; upper = 300; step = 20; fahr = lower; care seteaza variabilele pe valorile lor de start. Instructiu nile individuale se termina cu punct si virgula. while (fahr <= upper) A Este testata conditia din paranteza. Daca ea este adevarata while(i<j) i = 2 * i; In ambele cazuri, instructiunile controlate de "while" sint decalate cu un tab, asa ca se observa de la prima privire ce instructiuni se gasesc in interiorul buclei. Decalarea scoate in evidenta structura logica a programului. Cu toate ca limbajul C este destul de liberal in ceea ce priveste pozitionarea instructiunilor, o tabulare potrivita si folosirea spatiilor albe sint critice in scrierea programelor usor citibile si de catre altii decit autorul lor. Temperatura celsius este calculata si asignata lui "celsius" prin instructiunea: celsius = (5.0 / 9.0) * (fahr - 32.0); Ratiunea pentru folosirea lui (5.0/9.0) in locul mai simplei Regulile detaliate pentru conversiile de intregi in flotante sint date in Capitolul 2. Acum sa notam doar ca asignarea fahr = lower; si testul while (fahr <= upper) vor lucra amindoua asa cum ne asteptam "int" este convertit in Acest exemplu arata deasemenea putin mai multe despre modul de lucru al lui "printf". Ea este o functie de conversie de format cu scop general, care va fi descrisa complet in Capitolul 7. Primul sau argument este un sir de caractere ce se vor tipari, fiecare caracter % indicind argumentele (al doilea, al treilea) ce se va substitui si forma in care se vor tipari. De exemplu, in instructiunea printf("%4.0f %6.1f\n", fahr, celsius); specificatia de conversie "%4.0f" spune ca un numar flotant va fi tiparit intr-un spatiu de cel putin patru caractere, cu zero cifre dupa punctul zecimal. "%6.1f" descrie un alt numar care va ocupa cel putin sase spatii, cu o cifra dupa punctul zecimal, analog cu F6.1 din FORTRAN sau F(6,1) din PL/1. Parti din specificator pot fi omise: "%6f" arata ca numarul are o lungime de cel putin 6 caractere; "%.2f" cere doua pozitii dupa punctul zecimal, dar lungimea lui nu este supusa restric tiilor; "%f" spune doar sa se tipareasca numarul ca flotant. Fiindca veni vorba, "printf" NU face parte din limbajul C. Exercitiul 1.3. Modificati programul de conversie temperaturi pentru a scrie un antet la inceputul tabelei de conversie. Exercitiul 1.4. Scrieti un program care sa tipareasca tabela corespunzatoare Celsius - Fahrenheit. 1.3. Instructiunea For Asa cum probabil va asteptati, exista o multime de moduri pentru a scrie un program; haideti sa incercam o alta varian ta a programului de conversie de temperatura : main() /* Fahrenheit-Celsius table */ Aceasta va produce aceleasi rezultate dar, cu siguranta, arata altfel decit prima. O modificare esentiala este eliminarea majori tatii variabilelor; a ramas numai "fahr", declarata ca "int"
Aceasta ultima schimbare este un exemplu pentru o regula generala in C - in orice context in care este permisa folo sirea valorii unei variabile de un anumit tip, se poate folosi o expresie de acel tip. Deoarece al treilea argument al lui "printf" trebuie sa fie o valoare flotanta pentru a se potrivi cu "%6.1f", orice expresie flotanta poate apare pe locul ei. Instructiunea "for" este o bucla, o generalizare a lui fahr = 0 se face o data, inainte ca bucla propriu-zisa sa inceapa. A doua parte este testul sau conditia care controleaza bucla: fahr <= 300 Este evaluata aceasta conditie; daca ea este adevarata, este executat corpul buclei (la noi, o singura "printf"). Urmeaza apoi pasul de reinitializare fahr = fahr + 20 care este executat si apoi conditia este reevaluata. Bucla se termina atunci cind conditia devine falsa. La fel ca si la instructiunea "while", corpul buclei poate fi alcatuit dintr-o singura instructiune sau dintr-un grup de instructiuni inclus intre acolade. Partile de initializare si reinitializare pot fi o singura expresie. Alegerea intre "while" si "for" este arbitrara, bazata pe ceea ce ne pare noua a fi mai clar. Instructiunea "for" este potrivita in mod uzual pentru buclele in care initializarea si reinitia lizarea sint instructiuni unice si logic inrudite deoarece este mai compacta decit "while" si pastreaza instructiunile de control al buclei intr-un singur loc si impreuna. Exercitiul 1.5. Modificati programul de conversie temperatura pentru a tipari tabela in ordine inversa, adica de la 300 de grade la zero. 1.4. Constante simbolice Vom face o observatie finala inainte de a parasi pentru todeauna programul de conversie de temperatura. E o practica proasta aceea de a inmorminta "numere magice" ca 300 sau 20, intr-un program; ele transmit putina informatie cuiva care va citi programul mai tirziu si este greu sa le modificam intr-o maniera sistematica. Din fericire, C poseda o modalitate de a evita astfel de numere magice. Cu ajutorul constructiei "#define", se pot defini la inceputul programului nume sau constante simbolice, care sint un sir particular de caractere. Dupa aceea, compilatorul va inlocui toate aparitiile nepuse intre ghilimele ale numelui, prin sirul corespunzator. Inlocui rea efectiva a numelui poate fi orice text; ea nu se limiteaza la numere. #define LOWER 0 /* lower limit of the table */ Cantitatile LOWER, UPPER si STEP sint constante, asa incit ele nu apar in declaratii. Numele simbolice se scriu in mod normal cu litere mari, asa ca ele pot fi usor distinse de numele de variabile care se scriu cu litere mici. Sa notam ca la sfirsitul unei definitii NU se pune punct si virgula. Deoarece intreaga linie de dupa numele definit este substituita, in instructiunea "for" ar exista prea multe punct si virgule. 1.5. O colectie de programe utile Vom considera in cele ce urmeaza o familie de pro grame inrudite pentru efectuarea de operatii simple asupra datelor alcatuite din caractere. Vom vedea ca multe programe sint doar versiuni extinse ale prototipurilor pe care le vom discuta aici. Introducere si extragere de caractere Biblioteca standard poseda functii pentru citirea si scrierea unui caracter la un moment dat. "getchar()" aduce urmatorul caracter de intrare de fiecare data cind este apelata si returneaza acel caracter ca si valoare a ei. Adica, dupa c=getchar() variabila "c" contine urmatorul caracter de intrare. Caracte rele vin in mod normal de la terminal, dar aceasta nu ne intereseaza pina in Capitolul 7. Functia "putchar(c)" este complementara lui "getchar()": putchar(c) tipareste continutul variabilei "c" pe un mediu de iesire, in mod normal, tot pe terminal. Apelurile la "putchar" si "printf"
pot fi intercalate; iesirea va apare in ordinea in care s-au facut apelurile. Copiere de fisiere Date "getchar" si "putchar", veti putea scrie o cantitate
surprin zatoare de cod util, fara a sti nimic despre operatiile de I/O. citeste_un_caracter Convertind aceasta in limbajul C, obtinem: main() /* copy input to output; 1st version */ Operatorul relational "!=" inseamna "diferit de". #define EOF -1 sau pentru ca el sa lucreze corect. Folosind constanta simbolica c = getchar() poate fi folosita intr-o expresie; valoarea sa este pur si simplu valoarea ce se asigneaza partii stingi. Daca asignarea unui caracter lui c se pune in partea de test a unui "while", programul de copiat fisiere poate fi scris astfel: main() /* copy input to output; 2nd version */ Programul citeste un caracter, il asigneaza lui "c" si apoi tes teaza daca acesta a fost semnalul de sfirsit de fisier. Daca nu a fost, corpul buclei "while" este executat, tiparindu-se caracterul si bucla se repeta. Cind semnalul de sfirsit de fisier este atins in fine, bucla "while" se termina, terminindu se totodata si programul "main". Aceasta versiune centralizeaza intrarile - nu mai apare decit un singur apel la "getchar"- si restringe programul. Plasarea unei asignari intr-un test constituie unul din locurile unde C permite o concizie uluitoare. (Este posibil sa mergeti si mai departe, creind un cod impenetrabil, tendinta pe care noi incer cam sa nu o incurajam). Este important sa recunoastem ca parantezele ce includ asignarea sint absolut necesare. Ponderea lui "!=" este mai mare decit aceea a lui "=" ceea ce inseamna ca, in absenta parantezelor, testul relational "!=" va fi facut inaintea asig narii "=". Asa ca instructiunea c = getchar() != EOF este echivalenta cu c = (getchar() != EOF) Aceasta are un efect nedorit, prin setarea lui "c" pe 0 sau 1, dupa cum apelul lui "getchar " a intilnit sau nu sfirsitul de fisier. (Mai multe despre acestea se vor vedea in Capitolul 2). Contorizarea caracterelorUrmatorul program va contoriza caracterele; el este o mica elaborare a programului de copiere. main() /* count characters in input */ Instructiunea ++nc; ne introduce un nou operator "++" care inseamna, increment cu 1.
main() /* count characters in input */ "printf" foloseste "%f" atit pentru "float" cit
si pentru Corpul buclei "for" este in cazul acesta vid, deoarece toata munca este facuta in partile de test si reinitializare. Dar regulile gramaticale ale limbajului C pretind ca o instructiune Inainte de a parasi programul de contorizare caractere, sa observam ca daca intrarea nu contine nici un caracter, testul din "while" sau "for" esueaza la primul apel la getchar
si deci rezultatul programului este zero, ceea ce este corect. Aceasta este o observatie importanta. Unul din lucrurile frumoase care se pot spune despre "while" si despre "for" este cela ca ele
testeaza la inceputul buclei, inainte de a prelucra corpul buclei. Daca nu este nimic de facut, nimic nu se face, chiar daca aceasta inseamna ca nu se va parcurge corpul buclei nicio data. Programele vor actiona inteligent atunci cind vor minui intrari de tipul "nici un caracter". Instructiunile "while"
si Contorizarea liniilor Urmatorul program contorizeaza liniile pe care le primeste ca intrare. Liniile de intrare se presupun a fi terminate cu un caracter "linie noua" \n adaugat cu sfintenie la fiecare linie scrisa. main() /* contorizarea liniilor in intrare */ Corpul buclei "while" consta acum dintr-un "if",care la rindul ei controleaza incrementarea ++nl. Instructiunea "if" testeaza conditia din paranteza si, daca este adevarata, se executa in structiunea (sau grupul de instructiuni dintre acolade) care urmeaza. Am aliniat iarasi, ca sa aratam ce este controlat de cine (ce). Semnul dublu de egal "==" este in C notatia pentru "este egal cu" (ca si .EQ. din FORTRAN). Acest simbol este folosit pentru a distinge testul de egalitate de egal simplu (=) folosit pentru asignare. Deoarece asignarea este cam de doua ori mai frecventa in C decit testul de egalitate, este normal ca si operatorul de asignare sa fie jumatate din cel de egalitate, ca lungime. Orice caracter singur poate fi scris intre apostrofuri, pentru a produce valoarea numerica a caracterului in codul de carctere al calculatorului; acesta se numeste constanta de caracter. Asa de exemplu, 'A' este o constanta de caracter; in setul de caractere ASCII, valoarea sa este 65, reprezentarea interna a caracterului A. Desigur 'A' este de preferat lui 65: semnificatia lui este evidenta si independenta de orice set parti cular de caractere. Secventele escape folosite in sirurile de caractere sint si ele legale in constantele de caracter, asa ca in teste si in expresii aritmetice '\n' tine locul caracterului "linie noua". Exercitiul 1.6. Scrieti un program care sa numere blankurile, taburile si new-line-urile. Exercitiul 1.7. Scrieti un program care sa copieze intrarea in iesire,inlocuind fiecare sir de unul sau mai multe blankuri cu un singur blank. Exercitiul 1.8. Scrieti un program care sa inlocuiasca fiecare tab printr-o secventa >,backspace,- care se va tipari ca "->" s fiecare backspace prin secventa similara "<-". Contorizarea de cuvinte Al patrulea program din seria de programe utile va contoriza linii, cuvinte si caractere, un singur cuvint fiind definit ca orice secventa de caractere care nu contine blanc, tab sau linie noua (acesta este de fapt un schelet al programului utilitar "wc" din UNIX). #define YES 1 De fiecare data cind programul intilneste primul caracter al unui cuvint, il contorizeaza. Variabila "inword" inregistreaza de cite ori programul este intr-un cuvint sau nu ; initial el Linia nl = nw = nc = 0; seteaza toate cele trei variabile pe zero. Acesta nu este un caz special ci doar o consecinta a faptului ca o asignare asociaza de la dreapta spre stinga. Este ca si cind am fi scris; nc = (nl = (nw = 0)); Operatorul || inseamna SAU, asa ca linia if(c == ' ' || c == '\n' || c == '\t'); spune ca "daca c este un blanc sau c este o linie noua sau c este un tab...". (Secventa escape \t este reprezentarea vizibila a caracterului tab).Exista un operator corespunza tor && pentru SI. Expresiile conectate prin && sau || sint evaluate de la stinga la dreapta si evaluarea se opreste atunci cind se cunoaste adevarul sau falsul expresiei. Astfel daca c contine un blanc, nu mai este nevoie sa testam daca el contine o line noua sau un tab, asa ca testele acestea nu se mai fac. In particular, aceasta nu este important aici, dar este foarte semni ficativ in multe situatii complicate, asa cum vom vedea in curind. Exemplul nostru foloseste deasemenea instructiunea "else", care specifica o actiune alternativa ce trebuie executata daca partea de conditie unei instructiuni "if" este falsa. Forma generala este: if (expresie) instructiune1 else instructiune2 Una si numai una din instructiunile asociate cu if-else se executa. Daca "expresia" este adevarata, se executa "instruc tiunea-1"; daca nu, se executa "instructiunea-2". Fiecare "in structiune" poate fi, de fapt, mult mai complicata. In exemplul nostru instructiuea de dupa "else" este un "if" care controleaza doua instructiuni in paranteze. Exercitiul 1.9. Cum veti testa programul de contorizare cuvinte? Care sint unele dintre limitele lui ? Exercitiul 1.10. Scrieti un program care sa tipareasca cuvin tele introduse,cite unul pe linie. Exercitiul 1.11. Revizuiti programul de contorizare cuvinte pentru a folosi o mai buna definitie a "cuvintului", de exemplu o secventa de litere, cifre si apostrofuri care incepe cu o litera. 1.6. Tablouri Vom scrie acum un program care va contoriza aparitiile fiecarei cifre, a fiecarui caracter de spatiere (blanc, tab, linie noua) si a tuturor celorlalte caractere. Desigur, este un program artificial, dar ne va permite sa ilustram mai multe aspec te ale lui C intr-un singur program. Exista 12 categorii de intrari, asa ca ne este mai convenabil sa folosim un tablou pentru a tine numarul de aparitii a fiecarei cifre, decit sa folosim 10 variabile individuale. Iata acum o versiune a acestui program: main() /* contorizeaza cifre, spatii albe, alte caractere */ Declaratia int ndigita10i; spune ca ndigit este un tablou de 10 intregi. Indicii de tablou intodeauna incep de la zero in C (spre deosebire de if (c >= '0' && c <= '9') ... determina daca un caracter din c este cifra. Daca el este cifra, valoare numerica a acelei cifre este c - '0' Acest algoritm functioneaza bine numai daca '0', '1', etc sint pozitive si in ordine crescatoare, si intre '0' si '9' nu se gaseste altceva decit cifre. Din fericire, aceasta este ade varul pentru toate seturile de caractere conventionale. Prin definitie, calculele aritmetice care implica tipuri Decizia se ia asupra caracterului (daca el este o cifra, un caracter de spatiere sau altceva) in secventa: if (c >= '0' && c <= '9') Constructia de tipul if (conditie) instructiune else if (conditie) instructiune else instructiune apare frecvent in programe ca o modalitate de a exprima deci ziile multiple. Codul se citeste simplu de sus in jos pina cind o conditie este indeplinita; in acest punct, se executa partea corespunzatoare de "instructiune" si intreaga constructie este terminata. (Desigur ca "instructiune" pot fi mai multe in structiuni incluse intre paranteze). Daca nici una din con ditii nu este indeplinita, instructiunea care urmeaza dupa ultimul else if (conditie) instructiune grupate intre "if"-ul initial si "else"-ul final. Ca o ches tiune de stil, va sfatuim sa formati aceste constructii asa cum le-am facut si noi, astfel incit deciziile lungi sa nu ajunga pe marginea din dreapta a paginii. Instructiunea "switch", care va fi prezentata in Capitolul 3, reprezinta un alt mod de a scrie o decizie multipla si este potrivita, in particular, cind conditia care se testeaza este simpla sau cind o expresie de caractere sau de intregi se potri veste cu o constanta dintr-un sir dat. Prin contrast, vom prezenta o versiune "switch" a acestui program in Capitolul 3. Exercitiul 1-12. Scrieti un program pentru a tipari histogra ma lungimilor cuvintelor care apar la intrare. Este cel mai usor sa desenati histograma orizontal; o orientare verticala este mai laborioasa. 1.7. Functii In C o functie este echivalenta cu o subrutina sau cu o functie din FORTRAN sau cu o procedura din PL/1 sau PASCAL, etc. O functie reprezinta un mod convenabil de a incapsula anumite calcule intr-o cutie neagra care poate fi apoi folosita fara sa ne mai pese de ce se afla inauntru. Functiile sint singura modali tate de a face fata la complexitatea potentiala a programe lor mari. Cu functii scrise asa cum trebuie, este posibil sa ignoram "cum" este facuta o anumita treaba; ne este suficient sa stim "ce" anumita treaba este facuta. Limbajul C este proiectat pentru a face folosirea lor usoara, convenabila si eficienta; veti observa adesea o functie lunga numai de citeva rinduri, apelata o singura data, numai fiindca ea clarifica anumite porti uni de cod. Pina acum am folosit numai functii ca printf, getchar si putchar, care au fost scrise de altii pentru noi; este momentul sa ne scriem si noi propriile noastre functii. Deoarece limbajul C nu poseda un operator de exponentiere ca ** din FORTRAN sau PL/1, vom ilustra mecanismul de definire de functii scriind o functie putere "power(m,n)" care va ridica un intreg la o putere intrea ga pozitiva in n. Adica, valoarea lui power(2,5) este 32. Desi gur ca aceasta functie nu realizeaza toata treaba lui ** deoarece minuieste numai puteri pozitive ale intregilor mici, dar cel mai bine,este bine sa aprofundam un lucru la un moment dat. main() /* testeaza functia power */ Orice functie are o aceeasi forma: nume (lista de argumente, daca exista) declaratii de argumente, daca exista Functiile pot apare in orice ordine si intr-un fisier sursa sau in doua. Bineinteles, daca sursa apare in doua fi siere, veti avea mai multe de spus la compilare si incarcare decit daca e un singur fisier dar asta este o problema a sistemului de operare si nu un atribut al limbajului. Pentru moment vom presupune ca ambele functii se gasesc intr-un acelasi fisier, asa ca ceea ce ati invatat despre executia programelor C nu se modifica.Functia "power" este apelata de doua ori in linia printf("%d %d %d\n", i, power(2,i), power(-3,i)); Fiecare apel trimite doua argumente lui power, care de fiecare data returneaza un intreg care trebuie formatat si tiparit. Intr o expresie power(2,i) este un intreg, la fel ca si 2 si i. (Nu toate functiile produc o valoare intreaga; vom vedea aceasta in int x,n; care urmeaza liniei cu numele functiei. Declaratiile de argumente urmeaza sa se situeze intre lista de argumente si acolada stinga deschisa A; fiecare declaratie se termina cu punct si virgula. Numele folosite de power pentru argumentele sale sint pur locale lui power si nu sint accesibile nici unei alte functii: alte rutine pot folosi aceleasi nume fara nici un conflict. Exercitiul 1.13. Scrieti un program care sa converteasca intrarea in litere mici, folosind o functie lower(c) care returneaza pe c, daca c nu este o litera, si valoarea "litera mica a lui c", daca c este o litera. 1.8. Argumente - apel prin valoare Un aspect al functiilor din limbajul C s-ar putea sa fie nefami liar programatorilor obisnuiti cu alte limbaje, in particular cu power(x,n) /*ridica pe x la puterea a n-a; n > 0;versiunea 2*/ int x, n; Argumentul n este folosit ca o variabila temporara, si este decrementat pina cind devine zero; nu mai este nevoie de varia bila i. Ceea ce se face cu n in interiorul lui power nu are nici un efect asupra argumentului cu care a fost apelata power initial. Cind este necesar, este posibil sa aranjam ca o functie sa modi fice o variabila in rutina apelanta. Apelandul trebuie sa dea adresa variabilei de setat (in mod tehnic, sa creeze un pointer la variabila),iar functia apelata trebuie sa declare argumentul ca fiind un pointer si sa refere variabila reala in mod indirect prin el. Vom discuta in detaliu aceste probleme in Capitolul 5. Cind numele unui tablou este folosit ca si argument, valoarea transmisa functiei este locatia sau adresa de inceput a tablo ului. (Nu se face nici o copiere de elemente de tablou). Indiciind aceasta valoare, functia poate avea acces si altera orice element al tabloului. Acesta este subiectul urmatoarei sectiuni. 1.9. Tablouri de caractereIn mod probabil, cel mai comun tip de tablouri in limbajul C este tabloul de caractere. Pentru a ilustra folosirea tablourilor de caractere si a functiilor care le manipuleaza, vom scrie un pro gram care citeste un set de linii si o tipareste pe cea mai lunga. while (mai exista o alta linie) if (este mai lunga decit linia anterioara) salveaza-o pe ea si lungimea ei tipareste linia cea mai lunga Aceasta schita ne arata clar ca programul se imparte in bucati. O bucata citeste o linie noua, o alta bucata o testea za, o alta o salveaza iar restul controleaza procesul. #define MAXLINE 1000 /* lungimea maxima a liniei */ main() /* gaseste linia cea mai lunga */ getline (s, lim) /* citeste linia in s, returneaza lungimea */ char sai; int lim; A int c, i; for(i = 0; i < lim - 1 && (c=getchar())!=EOF && c!='\n';++i) saii = c; if (c == '\n') A saii = c; copy(s1, s2) /* copiaza pe s1 in s2; s2 suficient de mare */ char s1ai, s2ai; main si getline comunica intre ele printr-o pereche de argumente si o valoare returnata. In getline, argumumentele sint declarate prin liniile: char sai; int lim; care spun ca primul argument este un tablou iar al doilea un intreg. Lungimea tabloului s nu este specificata in getline deoarece ea este determinata in main. "getline" foloseste in structiunea return pentru a trimite o valoare inapoi apelan tului, la fel cum facea si functia power. Unele functii returneaza o valoare utila; altele, de exemplu copy, sint folosite numai pentru efectul lor si nu returneaza nici o valoare. getline pune caracterul \0 (caracterul nul, a carui valoare este zero) la sfirsitul tabloului pe care il creaza, pentru a marca sfirsitul sirului de caractere. Aceasta conventie este folosita de asemenea si de catre compilatorul C; cind o constanta sir de tipul "hello\n" este scrisa intr-un program C, compilatorul isi creaza un tablou de caractere continind carcterele sirului si terminat cu \0, astfel incit o functie, de exemplu printf, poate sa-i determine sfirsitul. ------------------------------ | h | e | l | l | o | \n | \0 | Specificatorul de format %s din printf se asteapta la un sir reprezentat tocmai in aceasta forma. Daca examinati functia copy, veti descoperi ca si ea se bizuie de fapt pe terminarea argumentu lui sau de intrare s1 cu un \0 si ea copiaza acest caracter in argumentul de iesire s2. (Toate acestea presupun ca \0 nu este parte a unui text normal). Exercitiul 1.14. Revizuiti rutina main din programul precedent astfel incit ea sa tipareasca corect lungimea unei linii de intrare de o lungime arbitrara, si atita text cit este posibil de tiparit. Exercitiul 1.15. Scrieti un program care sa tipareasca toate liniile mai lungi de 80 de caractere. Exercitiul 1.16. Scrieti un program care sa elimine blancurile nesemnificative (cele de dupa un caracter diferit de blanc sau tab) din fiecare linie de intrare si care sa stearga liniile care contin numai blancuri. Exercitiul 1.17 Scrieti o functie reverse(s) care sa inverseze un sir de caractere s. Folositi-o pentru a scrie un program care isi inverseaza linie cu linie intrarea. 1.10 Domeniu; variabile externe Variabile din main (line, save, etc) sint private sau locale lui main; deoarece ele sint declarate in main, nici o alta functie nu poate avea acces direct la ele. La fel se intimpla si cu variabilele din alte functii de exemplu variabila i din getline nu are nici o legatura cu varibila i din copy. Fiecare variabila locala dintr-o rutina se naste numai atunci cind functia este apelata si"dispare" cind functia isi termina activitatea. Aceasta
este ratiunea pentru care astfel de variabile sint cunoscute uzual sint numele de variabile automate, urmind terminologia din alte limbaje. Vom folosi termenul de "automat" de aici inainte pentru a
ne referi la aceste variabile dinamice locale. (Capitolul 4 discu ta clasa de memorie "statica" in care variabilele locale isi pastreaza valoarea intre apelurile la functii). O variabila externa trebuie sa fie definita in afara oricarei functii; acest lucru face sa se aloce memorie reala pentru ea. Variabila trebuie deasemenea sa fie declarata in fiecare functie care vrea sa o foloseasca; aceasta se poate face fie printr-o declaratie explicita "extern", fie implicit prin context. Pentru a face discutia corecta, vom rescrie programul precedent, in care line, save, max vor fi declarate variabile externe. Aceasta va cere modificari in apeluri, in declaratii si in corpurile celor trei functii. #define MAXLINE 1000 /* marimea maxima a liniei de intrare */ char lineaMAXLINEi; /* linia de intrare */ char saveaMAXLINEi; /* cea mai lunga linie este salvata aici*/ int max; /* lungimea liniei celei mai mari */ main() /* gaseste linia cea mai lunga ;versiune specializata*/ copy() /* versiune specializata */ Variabilele externe din main, getline si copy sint defi nite de primele linii din exemplul de mai sus care declara tipul lor si provoaca o alocare de memorie pentru ele. Din punct de vedere sintactic, definitiile externe sint asemanatoare cu decla ratiile pe care le-am folosit anterior dar deoarece ele apar in afara functiilor, variabilele sint externe. Inainte ca o functie sa poata folosi o variabila externa, numele variabilei trebuie sa fie facut cunoscut functiei O modalitate pentru a face aceasta este scriind o declaratie "extern"; declaratia este identica cu cea de dinainte, avind insa in plus cuvintul cheie extern. Puteti nota ca am folosit cu grija cuvintele "declaratie" si Fiindca veni vorba, exista o tendinta de a face totul cu ajutorul variabilelor externe deoarece ele par a simplifica toate comunicatiile - listele de argumente sint scurte si variabilele sint intodeauna acolo cind aveti nevoie de ele. Dar variabilele externe sint intodeauna acolo chiar si cind nu aveti nevoie de ele. Acest stil de a codifica este plin de pericole deoarece el conduce la programe ale caror conexiuni de date nu sint evidente clar - variabilele pot fi modificate in moduri neastep tate si chiar inadvertente iar programul devine greu de modi ficat daca acest lucru este necesar. A doua versiune a progra mului care cauta linia cea mai luunga este inferioara primei, partial din aceste motive, si partial deoarece ea distruge genera litatea a doua functii atit de utile introducind in ele numele unor variabile pe care le vor folosi. Exercitiul 1.18 Testul din instructiunea for din functia getline de mai sus este aproape de neinteles. Rescrieti programul pentru a-l face mai clar dar pastrati acelasi com portament la sfirsitul fisierului sau la depasire de buffer. Este acest comportament cel mai adecvat ? 1.11 Rezumat In acest punct am acoperit ceea ce, conventional, poate fi numit esenta lui C. Cu aceste citeva caramizi, este posibil sa scrieti programe utile de marime considerabila dar ar fi o buna idee daca v-ati odihni mai mult inainte de a face asa ceva. Exercitiul 1.19. Scrieti un program "detab" care inlocuieste taburile din intrare cu numarul potrivit de blancuri pentru a sari pina la urmatorul stop de tab. Presupuneti un set fixat de stopuri de tab, fie din n in n pozitii. Exercitiul 1.20. Scrieti un program "entab" care inlocuieste si ruri de blancuri cu numarul minim de taburi si blancuri pentru a obtine o aceasi spatiere. Folositi aceleasi stopuri de tab ca si detab. Exercitiul 1.21. Scrieti un program pentru a "impaturi" li niile de intrare lungi dupa ultimul caracter neblanc care apare inainte de a n-a coloana a intrarii, unde n este un parametru. Asigurati-va ca programul dumneavoastra lucreaza inte ligent cu liniile foarte lungi, chiar daca nu e nici un tab sau blanc inainte de coloana specificata. Exercitiul 1.22. Scrieti un program care sa elimine toate comenta riile dintr-un program C. Nu uitati sa minuiti adecvat sirurile dintre ghilimele si constantele de caractere. Exercitiul 1.23. Scrieti un program pentru a verfica un program C din punct de vedere al erorilor de sintaxa rudimentare ca de exemplu: paranteze neperechi. Nu uitati ghilimelele, atit cele simple cit si cele duble si comentariile. (Acest program este greu daca il faceti la cazul cel mai general.) |
||||||
|
||||||
|
||||||
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite |
|