Document, comentariu, eseu, bacalaureat, liceu si facultate
Top documenteAdmitereTesteUtileContact
      
    


 


Ultimele referate adaugate

Adauga referat - poti sa ne ajuti cu un referat?

Politica de confidentialitate



Ultimele referate descarcare de pe site
  CREDITUL IPOTECAR PENTRU INVESTITII IMOBILIARE (economie)
  Comertul cu amanuntul (economie)
  IDENTIFICAREA CRIMINALISTICA (drept)
  Mecanismul motor, Biela, organe mobile proiect (diverse)
  O scrisoare pierduta (romana)
  O scrisoare pierduta (romana)
  Ion DRUTA (romana)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  Starea civila (geografie)
 

Ultimele referate cautate in site
   domnisoara hus
   legume
    istoria unui galban
   metanol
   recapitulare
   profitul
   caract
   comentariu liric
   radiolocatia
   praslea cel voinic si merele da aur
 
despre:
 
INITIERE IN C
Colt dreapta
Vizite: ? Nota: ? Ce reprezinta? Intrebari si raspunsuri
 
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 ()
A printf("hello, world\n");
S

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
>as 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
>run hello

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.
In exemplul nostru, "main" este o astfel de functie. In mod normal aveti libertatea de a da functiilor ce nume doriti, dar
"main" este un nume special - programul dumneavoastra se va executa de la inceputul lui "main". Aceasta inseamna ca fiecare program trebuie sa aibe un "main" undeva, ca "main" va invoca in mod obisnuit alte functii pentru a-si realiza scopul, unele venind din acelasi program iar altele din biblioteci ce contin functii scrise anterior.

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,
PASCAL, etc. O functie este apelata prin nume, urmate de o lista de argumente in paranteze.
Nu exista instructiunea CALL ca in FORTRAN sau PL/1. Parante zele trebuie sa fie prezente chiar daca nu exista argumente.

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
"\n" (un experiment care merita facut), veti observa ca iesirea dumneavoastra nu se termina cu o linie noua. Singurul mod de a avea caracterul "linie noua" in "printf" este "\n" ca argument. Daca incercati ceva de tipul

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()
A printf("hello, "); printf("world"); printf("\n");
S

pentru a produce o iesire identica.
Sa notam ca "\n" reprezinta un singur caracter. O "secventa escape" ca de exemplu "\n" este in general un mecanism extensibil pentru reprezentarea caracterelor greu de obtinut sau invizibile. Printre alte secvente escape, limbajul C poseda:
\t pentru tab, \b pentru backspace, \" pentru apostrof dublu si \\ pentru backspace.

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.

1.2. Variabile si aritmetica

Urmatorul program tipareste tabela de temperaturi Fahrenheit si echivalentele lor in centigrade sau grade Celsius, folosind formu la: C = (5 / 9) * (F - 32).

0 -17.8
20 -6.7
40 4.4
60 15.6
...
260 126.7
280 137.8
300 148.9

Iata acum si programul:

/* Print Fahrenheit-Celsius table for f = 0, 20, ..., 300 */ main()
A int lower, upper, step; float fahr, celsius; lower = 0; /* lower limit of temperature table */ upper = 300; /* upper limit */ step = 20; /* step size */ fahr = lower;
while (fahr <= upper) A celsius = (5.0 / 9.0) * (fahr - 32.0); printf("%4.0f %6.1f\n", fahr, celsius); fahr = fahr + step;
S
S

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;
"float" denota virgula mobila, adica numere care pot avea parte fractionara. Precizia atit pentru "int" cit si pentru "float" depinde de calculatorul pe care-l folositi. Pentru PDP-11, de exemplu, un "int" este un numar cu semn de 16 biti, adica un numar cuprins intre -32768 si +32767 . Un numar "float" este o cantitate ce se reprezinta pe 32 de biti ceea ce revine la aproximativ sapte cifre semnificative, cu magnitudinea cuprinsa aproximativ intre 10 ^ -38 si 10 ^ +38. Capitolul 2 da olista a acestor marimi pentru alte calculatoare.
Pe linga tipurile "int" si "float", limbajul C poseda si alte tipuri de date fundamentale :

"char" caracter - un singur octet
"short" intreg scurt
"long" intreg lung
"double" numar flotant dubla precizie

Marimea acestor obiecte este deasemenea dependenta de calculator; detalii se dau in Capitolul 2. Exista deasemenea "tablouri",
"structuri" si "uniuni" de asemenea tipuri de baza, "pointeri" la ele si "functii" care le returneaza si cu toate ne vom intilni in aceasta carte.

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.
Fiecare linie a tabelei este calculata in acelasi fel, asa ca vom folosi o bucla care se repeta odata pe linie; acesta este scopul instructiunii "while":

while (fahr <= upper) A
...
S

Este testata conditia din paranteza. Daca ea este adevarata
(fahr este mai mic sau egal cu upper), este executat corpul buclei (toate instructiunile incluse intre parantezele A si
S). Apoi conditia este retestata si, daca este adevarata, corpul este executat din nou . Cind testul devine fals (fahr este mai mare decit upper), bucla se termina si executia conti nua cu instructiunea care urmeaza buclei. Deoarece in acest program nu mai exista alte instructiuni care sa succeada bucla, executia lui se termina.
Corpul unei bucle while poate fi alcatuit din mai multe instructiuni incluse intre acolade, ca in programul de mai sus sau dintr-o singura instructiune, fara paranteze, ca in exemplul de mai jos:

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.
Recomandam scrierea unei singure instructiuni pe un rind si lasarea de spatii (uzual) in jurul operatorilor. Pozitia acolade lor este mai putin importanta; noi am ales unul dintre stilurile cele mai populare. Alegeti-va un stil care va convine, apoi folo siti-l consecvent.

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
5/9 este aceea ca in C, ca si in multe alte limbaje, impartirea intreaga trunchiaza rezultatul, asa ca orice parte fractionara este eliminata. Astfel 5/9 este zero si tot asa ar fi fost toate temperaturile. Un punct zecimal intr-o constanta indica faptul ca aceasta este flotanta si deci 5.0/9.0 este 0.5555... ceea ce am si dorit. Am scris deasemenea 32.0 in loc de 32, chiar daca deoarece
"fahr" este "float", 32 ar fi convertit automat in "float" inainte de scadere. Ca o problema de stil, este bine sa scriem constantele flotante cu punct zecimal explicit chiar cind au valori intregi; aceasta accentueaza natura lor flotanta pentru cititorii umani si ne asigura ca si compilatorul va gindi in acelasi mod ca si noi.

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
"float" inainte de executia operatiei.

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.
"printf" recunoaste deasemenea: "%d" pentru intregi zecimali,
"%o" pentru numere octale,"%x" pentru hexazecimale, "%c" pentru un caracter, "%s" pentru sir de caractere si "%%" pentru insusi semnul "%".
Fiecare constructie cu % in primul argument al lui "printf" face pereche cu al doilea, al treilea,... argument. Aceste perechi trebuie sa corespunda ca numar si tip, altfel veti avea surpriza unor rezultate lipsite de inteles.

Fiindca veni vorba, "printf" NU face parte din limbajul C.
Nu exista definite in C intrari si iesiri. Nu e nimic magic in legatura cu "printf" ea este pur si simplu o functie utila care face parte din biblioteca standard de rutine care sint in mod normal accesibile programelor C. Cu scopul de a ne concentra asupra limbajului C, nu vom spune prea multe despre opera tiile de I/O pina in Capitolul 7. In particular, vom amina descrierea intrarilor cu format pina atunci. Daca aveti de introdus numere, cititi descrierea functiei "scanf" din Capitolul
7, sectiunea 7.4; "scanf" este foarte asemanatoare cu "printf" atit doar ca ea citeste intrari in loc sa scrie iesiri.

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 */
A int fahr; for (fahr = 0; fahr <= 300; fahr = fahr + 20) printf("%4d %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32));
S

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"

(observati specificatorul "%d" in printf). Limitele inferioara si superioara si marimea pasului apar doar ca si constante in instructiunea "for", ea insasi o constructie noua, iar expresia care calculeaza temperatura Celsius apare acum ca al treilea argument din "printf" in loc de a fi o instructiune de asignare separata.
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
"while". Daca o comparati cu "while", aceasta afirmatie va va fi clara. Ea contine trei parti separate prin punct si virgula. Prima parte

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 */
#define UPPER 300 /* upper limit */
#define STEP 20 /* step size */ main() /* Fahrenheit-Celsius table */
A int fahr; for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP) printf("%4d %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32));
S

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.
Ca si in cazul lui "printf", nu exista nimic special rela tiv la "getchar" si "putchar". Ele nu sint parti ale limbajului
C, dar sint universal disponibile.

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.
Cel mai simplu program este acela care-si copiaza intrarea in iesire, caracter cu caracter. Schitindu-l:

citeste_un_caracter
while (caracterul_nu_este_semnal_de_sfirsit_de_fisier) tipareste_caracterul_chiar_citit citeste_un_nou_caracter

Convertind aceasta in limbajul C, obtinem:

main() /* copy input to output; 1st version */
A int c; c = getchar();
while (c != EOF) A putchar(c); c = getchar(c);
S
S

Operatorul relational "!=" inseamna "diferit de".
Principala problema este detectarea sfirsitului de in trare. Prin conventie, "getchar" returneaza o valoare care nu este un caracter valid atunci cind intilneste sfirsitul intra rii; in acest mod, programele pot detecta cind s-au terminat intrarile. Singura complicatie, o neplacere serioasa de fapt este aceea ca exista doua conventii ce se folosesc uzual pentru valoarea sfirsitului de fisier. Noi am evitat aceasta folo sind deobicei numele simbolic de EOF pentru aceasta valoare, oricare ar fi fost ea. In practica, EOF poate fi sau -1 sau 0, asa ca programul trebuie sa fie precedat de una din declaratiile de mai jos:

#define EOF -1 sau
#define EOF 0

pentru ca el sa lucreze corect. Folosind constanta simbolica
EOF pentru a reprezenta valoarea pe care o ret/neaza getchar cind intilneste sfirsitul de fisier, ne asiguram ca numai un singur lucru din program depinde de valoarea numerica specificata.
De asemenea il declaram pe "c" ca fiind "int", nu "char", pentru ca el sa poata pastra valoarea pe care o returneaza
"getchar". Cum vom vedea in Capitolul 2, aceasta valoare este normal un "int", deoarece ea trebuie sa fie capabila sa-l repre zinte si pe EOF in plus fata de toate char-urile posibile.
Programul de copiere ar putea fi de fapt scris mult mai concis de catre un programator experimentat in limbajul C. In C, o asignare ca

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 */
A int c
while ((c = getchar()) != EOF) putchar(c);
S

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 caracterelor

Urmatorul program va contoriza caracterele; el este o mica elaborare a programului de copiere.

main() /* count characters in input */
A long nc; nc = 0;
while (getchar() != EOF)
++nc; printf("%ld\n",nc);
S

Instructiunea

++nc;

ne introduce un nou operator "++" care inseamna, increment cu 1.
Se putea scrie si "nc = nc+1", dar "++nc" este mai concisa si adesea mai eficienta. Exista un operator corespunzator, "--" pentru decrementare cu 1. Operatorii "++" si "--" pot fi atit operatori prefix, cit si sufix ("nc++"); aceste doua forme au valori diferite in expresii asa cum se va arata in Capitolul 2, dar "++nc" si "nc++" il incrementeaza amindoi pe "nc".
Programul de contorizare de caractere acumuleaza numarul de caractere intr-o variabila "long" in loc de "int". La PDP-11, valoarea maxima pentru un intreg este 32767 si s-ar putea ca sa dam peste o depasire de contor daca-l declaram intreg; pe
Honeywell si pe IBM, "long" si "int" sint sinonime si mult mai mari. Specificatorul de conversie "%ld" semnaleaza lui
"printf" ca argumentul corespunzator este un intreg "long".
Pentru a face fata la numere chiar si mai mari, se poate folosi o declaratie de "double" ("float" de lungime dubla). Vom folosi, deasemenea, instructiunea "for" in locul lui "while" pentru a ilustra un alt mod in scrierea buclei.

main() /* count characters in input */
A double nc ; for (nc = 0; getchar() != EOF; ++nc)
; printf("%.0f\n", nc);
S

"printf" foloseste "%f" atit pentru "float" cit si pentru
"double"; "%.0f" suprima tiparirea partii fractionare inexistente.

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
"for" sa aiba un corp. Punctul si virgula ce apare izolat pe o linie, in mod tehnic o instructiune nula, este pus tocmai pentru a satisface aceasta cerere. Noi l-am pus pe o linie separata tocmai pentru a-l face mai vizibil.

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
"for" ne asigura ca vor face lucruri rezonabile si in conditii la limita.

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 */
A int c, nl; nl = 0;
while ((c = getchar()) != EOF) if(c == '\n')
++nl; printf("%d\n", nl);
S

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".
Sa notam ca '\n' este un singur caracter si, in expresii, este echivalent cu un singur intreg; pe de alta parte, "\n" este un sir de caractere care, intimplator, contine un singur caracter. Subiectul comparatiei intre siruri si caractere este discutat mai departe in Capitolul 2.

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 "<-".

Aceasta face taburile si backspace-urile vizibile.

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
#define NO 0 main() /*contorizare linii, cuvinte si caractere la intrare*/
A int c, nl, nw, nc, inword; inword = NO; nl = nw = nc = 0;
while ((c = getchar()) != EOF) A
++nc; if(c == '\n')
++nl; if(c == ' ' || c == '\n' || c == '\t') inword = NO; else if (inword == NO) A inword = YES;
++nw;
S
S printf("%d %d %d'\n", nl, nw, nc);
S

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
"nu este intr-un cuvint " si variabilei i s-a asignat valoarea
NO. Preferam constantele simbolice YES si NO valorilor literale 1 si 0 deoarece ele fac programul mai usor citibil. Desigur ca intr un program mic ca acesta diferenta este mica, dar intr-un program mai mare cresterea in claritate merita micul efort supli menar de a-l scrie in acest mod de la inceput. Veti vedea deasemenea ca este mai usor sa efectuati modificari masive in programe in care numerele apar numai ca si constante simbolice.

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 */
A int c, i, nwhite, nother; int, ndigita10i; nwhite = nother = 0; for (i = 0; i < 10; ++i) ndigitaii = 0;
while ((c = getchar()) != EOF) if (c >= '0' && c <= '9')
++ndigitac-'0'i; else if (c == ' ' || c == '\n' || c == '\t')
++nwhite; else
++nother; printf("digits ="); for (i = 0; i < 10; ++i) printf(" %d", ndigitaii); printf("\nwhite space = %d, other = %d\n", nwhite, nother);
S

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
FORTRAN sau PL/1 unde incepe de la unu), asa ca elementele tabloului sint ndigita0i, ndigita1i , ...,ndigita9i. Acestea se reflecta in buclele "for", care initializeaza si tiparesc tabloul.
Un indice poate sa fie orice expresie intreaga, inclusiv desi gur variabilele intregi ca "i", sau constantele intregi.
Acest program particular se bazeaza mult pe proprietatile reprezentarii drept caractere a cifrelor. De exemplu, testul

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
"char" si "int", convertesc totul in tipul "int" inainte de prelucrare, asa ca variabilele si constantele de tip "char" sint esential identice cu tipul "int" in contexte aritmetice.
Acest fapt este aproape natural si convenabil; de exemplu: c '0' este o expresie intreaga cu o valoare intre 0 si 9 corespunza toare caracteruluidintre '0' si '9' depus in c, si deci este un indice valid pentru tabloul ndigit.

Decizia se ia asupra caracterului (daca el este o cifra, un caracter de spatiere sau altceva) in secventa:

if (c >= '0' && c <= '9')
++ndigitac-'0'i; else if (c == ' ' || c == '\n' || c == '\t')
++nwhite; else
++nother;

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" este executata daca este prezenta. Daca "else" final si
"instructiune" lipsesc (ca in programul nostru), nu are loc nici o actiune. Pot exista un numar arbitrar de constructii de tipul

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.
Iata acum functia "power" si un program principal care o foloseste, asa ca puteti vedea deodata intreaga structura.

main() /* testeaza functia power */
A int i; for (i = 0; i < 10; ++i) printf ('%d %d %d\n', i, power(2,i), power(-3,i));
S power(x,n) /* ridica pe x la puterea a n-a ; n > 0 */ int x, n;
A int i, p; p = 1; for (i = 1, i <= n; ++i) p = p * x; return(p);
S

Orice functie are o aceeasi forma:

nume (lista de argumente, daca exista) declaratii de argumente, daca exista
A declaratii instructiuni
S

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
Capitolul 4). In "power" argumentele trebuie sa fie declarate corespunzator cu tipul lor cunoscut. Aceasta este facuta de linia

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.
Aceasta este adevarat si pentru variabilele i si p: i din power (nu este legat prin nimic) nu are nici o legatura cu i din main.
Valoarea pe care power o calculeaza este returnata in main prin instructiunea "return", care este la fel ca in PL/1 .
Orice expresie poate apare intre paranteze. O functie nu trebuie sa returneze o valoare; o instructiune "return" fara nici o expresie cauzeaza transferul controlului, dar nu o valoare utila, spre apelant, asa cum face "iesirea dupa sfirsit" a unei functii prin atingerea parantezei drepte terminatoare.

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
FORTRAN sau PL/1.
In C, toate argumentele functiei sint transmise "prin va loare". Aceasta inseamna ca functiei apelate i se transmit valorile argumentelor in variabile temporare (de fapt intr-o stiva ) si nu i se transmit adresele lor. Aceasta duce la citeva proprietati diferite fata de limbajele cu "apel prin referinta" de tipul FORTRAN si PL/1, in care rutina apelata minuieste adresele argumentelor si nu valorile lor.
Principala distinctie este aceea ca in limbajul C, functia apelata nu poate altera o variabila in functia apelata; ea poate altera numai copia ei temporara si privata.
Apelul prin valoare este, cu toate acestea un avantaj si nu o obligatie. Uzual, el conduce la programe mai compacte cu mai putine variabile inutile, deoarece argumentele pot fi tratate ca variabile locale initializate convenabil in rutina apelata.
Drept exemplu, dam in continuare o versiune a functiei power care face uz de acest fapt.

power(x,n) /*ridica pe x la puterea a n-a; n > 0;versiunea 2*/ int x, n;
A int p; for (p = 1; n > 0; --n) p = p * x; return(p);
S

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 caractere

In 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.

Schita lui este destul de simpla:

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.
Deoarece lucrurile se impart asa de frumos, ar fi mai bine sa le scriem la fel. Pentru aceeasta, vom scrie la inceput o functie getline care va citi urmatoarea linie de la in trare; ea este generalizare a functiei getchar. Pentru a face functia utila si in alte contexte, vom incerca sa o scriem cit mai flexibil. In mod minim, getline va trebui sa returneze un semnal despre posibilul sfirsit de fisier; proiectind-o mai general,ea va trebui sa returneze lungimea liniei sau zero daca se intilneste sfirsitul de fisier. Zero nu este niciodata o lungime valida de linie, deoarece orice linie are cel putin un caracter, chiar si o linie ce contine numai caracterul "linie noua" are lungimea 1.
Cind gasim o linie care este mai lunga decit linia cea mai lunga gasita anterior, trebuie sa o salvam undeva. Aceasta suge reaza o a doua functie, copy, pentru a salva noua linie intr-un loc sigur.
In final, avem nevoie de un program principal care sa contro leze functiile getline si copy. Iata rezulatul:

#define MAXLINE 1000 /* lungimea maxima a liniei */ main() /* gaseste linia cea mai lunga */
A int len; /* lungimea liniei curente */ int max; /* lungimea maxima gasita pina acum */ char lineaMAXLINEi; /* linia curenta introdusa */ char saveaMAXLINEi; /* cea mai lunga linie salvata */ max = 0;
while ((len = getline(line, MAXLINE)) > 0) if (len > max) A max = len; copy(line, save);
S if (max > 0) /* s-a citit cel putin o linie */ printf("%s", save);
S

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;
++i; S saii = '\0'; return(i);
S

copy(s1, s2) /* copiaza pe s1 in s2; s2 suficient de mare */ char s1ai, s2ai;
A int i; i = 0;
while ((s2aii = s1aii) != '\0')
++i;
S

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).
Este demn de mentionat in trecere ca, un program, chiar si atit de mic ca acesta, prezinta unele probleme delicate de proiec tare. De exemplu, ce ar face main daca ar intilni o linie mai mare decit limita sa? getline lucreaza bine, adica se va opri atunci cind tabloul este plin chiar daca nu a intilnit nici un caracter
"linie noua". Testind lungimea si ultimul caracter returnat, main poate determina cind a fost linia prea lunga si apoi sa actioneze cum vrea. Pentru a scurta programul, am ignorat acest aspect.
Nu exista vre-o cale pentru utilizatorul lui getchar de a sti inainte cit va fi de lunga o linie de intrare, asa ca getline verifica daca nu s-a produs o depasire. Pe de alta parte, utilizatorul lui copy stie intodeauna (sau poate descoperi) cit este de mare sirul, asa ca nu trebuie sa adaugam la functie o verificare de erori.

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).
Deoarece variabilele automate vin si pleaca odata cu apeluri le de functii, ele nu-si pastreaza valoarea de la un apel la altul si trebuie initializate explicit inainte de fiecare intrare. Daca nu sint setate, rezultatele vor fi imprevizibile.
Ca o alternativa la variabilele automate, este posibil sa definim variabile care sa fie "externe" tuturor functiilor, adica, variabilele globale care sa fie accesate prin nume de orice functie care doreste sa o faca. (Acest mecanism este foarte asemanator cu COMMON din FORTRAN sau EXTERNAL din PL/1). Deoarece variabilele externe sint accesibile global, ele pot fi folosite in locul listelor de argumente pentru a comunica date intre functii.
Mai mult, deoarece variabilele externe exista permanent, si nu apar si dispar dupa cum functia este apelata sau s-a terminat, ele isi pastreaza valorile chiar si dupa ce s-a terminat functia care le-a setat.

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*/
A int len; extern int, max; extern char saveai; max = 0;
while ((len = getline() > 0) if (len > max) A max = len; copy();
S if (max > 0) /* a fost cel putin o linie */ printf("%s", save);
S getline() /* versiune specializata */
A int c, i; extern char lineai; for (i = 0; i < MAXLINE-1 && (c = getchar()) != EOF && c != '\n'; ++i) lineaii = c; if (c == '\n') A lineaii = c;
++i;
S lineaii = '\0'; return(i);
S

copy() /* versiune specializata */
A int i; extern char lineai, saveai; i = 0;
while ((saveaii = lineaii) != '\0')
++i;
S

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.
In anumite circumstante, declaratia "extern" poate fi omisa: daca definitia externa a variabilei apare in fisierul sursa inainte de folosirea ei intr-o functie particulara, atunci nu este necesara o declaratie "extern" in functie. Deci, declaratiile
"extern" din main, getline si cpoy sint redundante. De fapt prac tica uzuala consta in plasarea definitiilor tuturor variabilelor externe la inceputul fisierului sursa si apoi omiterea tuturor declaratiilor "extern".
Daca programul consta din mai multe fisiere sursa si o variabila este definita in fisierul 1 si folosita in fisie rul 2 atunci e nevoie de o declaratie "extern" in fisierul 2 pentru a conecta cele 2 aparitii ale variabilei. Acest subiect este discutat pe larg in capitolul 4.

Puteti nota ca am folosit cu grija cuvintele "declaratie" si
"definitie" cind ne-am referit la variabile externe in aceasta sectiune. "Definitiile" se refera la locul in care variabila este efectiv creata si i se asigneaza memorie; "declaratie" se refera la locul unde natura variabilei este declarata dar nu i se aloca memorie.

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.
Exercitiile care urmeaza au intetia de a va oferi sugestii pentru programe de o complexitate oarecum mai mare decit cele prezentate in acest capitol.
Dupa ce reusiti sa aveti sub control aceasta parte a lui C, ar fi demn de efortul dumneavoastra sa cititi mai depar te, deoarece caracteristicile lui C acoperite in urmatoarele citeva capitole sint cele in care puterea si expresivitatea limbajului devin aparente.

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.)


Colt dreapta
Creeaza cont
Comentarii:

Nu ai gasit ce cautai? Crezi ca ceva ne lipseste? Lasa-ti comentariul si incercam sa te ajutam.
Esti satisfacut de calitarea acestui document, eseu, cometariu? Apreciem aprecierile voastre.

Nume (obligatoriu):

Email (obligatoriu, nu va fi publicat):

Site URL (optional):


Comentariile tale: (NO HTML)


Noteaza documentul:
In prezent fisierul este notat cu: ? (media unui numar de ? de note primite).

2345678910

 
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite
Colt dreapta