Sunteți pe pagina 1din 140

1. Cum se scrie un program in C?

1.1. Un prim program

1.2. Variabile, expresii si asignari

1.3. Un exemplu de utilizare a variabilelor de tip float

1.4. Initializarea variabilelor

1.5. Folosirea directivei #define

1.6. Folosirea functiilor printf() si scanf()

1.7. Instructiunea "while"

1.8. Stilul de redactare al programelor

1.9. Erori de programare frecvente

1.10. Redirectarea intrarii si iesirii

1.11. Exercitii propuse spre implementare

======== Capitolul 1 ======== ========================= Cum se scrie un program (in C) ? =========================

Programele sunt scrise pentru a instrui masinile sa lucreze cu taskuri specifice sau sa rezolve probleme specifice. O procedura (descrisa pas cu pas) asociata unui task se numeste algoritm.

Programarea este activitatea de comunicare (codificare) a algoritmilor in calculatoare. Procesul de programare are (in general) patru pasi:

1. Specificarea task-ului;

2. Descoperirea unui algoritm pentru solutia sa;

3. Codificarea algoritmului in C;

4. Testarea codului.

Un calculator este o masina electronica digitala compusa din trei componente:

1. procesor (central processing unit sau CPU);

2. memorie;

3. dispozitive de intrare/iesire.

Procesorul lucreaza cu instructiuni care sunt inregistrate in memorie. Pe langa aceste instructiuni, in memorie sunt pastrate si date. Dispozitivele de intrare/iesire iau informatii de la agenti externi catre masina si produc informatii pentru acesti agenti. Dispozitivele de intrare sunt (de obicei):

1. tastatura;

2. discheta;

3. banda;

4. CD-ROM.

Dispozitivele de iesire sunt (de obicei):

1. ecranul terminalului;

2. imprimanta;

3. discheta;

4. banda;

5. CD-ROM.

Sistemul de operare consta intr-o colectie de programe speciale si are doua scopuri principale:

1. coordoneaza resursele (memoria, procesorul, imprimanta) masinii. De exemplu, daca un fisier

este creat pe disc sistemul de operare are grija de detaliile localizarii acestuia si memoreaza numele, lungimea si data creearii;

2. produce instrumente necesare utilizatorilor, multe dintre ele sunt folositoare utilizatorilor C. De

Un cod C se numeste cod sursa, iar un fisier ce contine un cod sursa se numeste fisier sursa. Dupa

ce a fost creat un fisier sursa, atunci se invoca un compilator de C. De exemplu, pentru sistemele MS-DOS se poate da comanda:

bc

ex1.c

sau tcc ex1.c

iar pentru unele sisteme UNIX:

cc ex1.c

Daca nu sunt erori in ex1.c, atunci aceasta comanda produce fisierul executabil asociat (ex1.exe). Acum acesta poate fi rulat (executat) cu numele sau (ex1 sau ex1.exe).

In continuare, vom preciza trei dintre trasaturile procesului de compilare (mentionam ca asupra

acestor notiuni, vom reveni cu detalii interesante intr-un capitol viitor):

1. invocarea preprocesorului;

2. invocarea compilatorului;

3. invocarea incarcatorului.

Preprocesorul modifica o copie a codului sursa prin includerea altor fisiere si facand alte schimbari. Compilatorul traduce aceasta in cod obiect folosit de incarcator pentru producerea fisierului

executabil final. Fisierul care contine codul obiect se numeste fisier obiect. Fisierele obiect, spre deosebire de fisierele sursa, nu se pot intelege asa usor. Cand spunem deci compilare, de fapt invocam preprocesorul, compilatorul si apoi incarcatorul. Dupa ce scriem un program, acesta trebuie compilat si testat. Daca sunt necesare modificari, atunci codul sursa trebuie editat din nou. Asadar, partea proceselor de programare consta din ciclul:

editare ---> compilare ---> executie |

^^

|

|

|

|

|

|

|

-----------------------

Un prim program

-----------------------

O sa incepem cu un exemplu de program C necesar pentru tiparirea unui sir pe ecran.

#include <stdio.h>

main()

{

printf("azi am inceput laboratoarele de C\n");

}

Folosind un editor de texte, presupunem ca am scris si salvat acest fisier numit "ex1.c". Cand programul este compilat si rulat atunci va apare pe ecran sirul:

azi am inceput laboratoarele de C

-------------

Explicatii:

-------------

1. #include <stdio.h> Liniile care incep cu "#" se numesc directive de preprocesare (precompilare). Acestea comunica cu preprocesorul. Aceasta directiva "#include" determina preprocesorul sa includa o copie a fisierului header "stdio.h" in acest punct al codului. Parantezele unghiulare din "<stdio.h>" indica ca acest

fisier se gaseste in biblioteca C (pentru compilatorul Borland 3.1 pentru MS-DOS, acesta se gaseste

in subdirectorul BC31/INCLUDE). Am inclus acest fisier deoarece acesta contine informatii despre

functia "printf()".

2.

main()

Fiecare program are o functie numita "main", care se executa intai. Parantezele ce urmeaza dupa "main" indica compilatorului ca aceasta este o functie.

3. {

Acolada stanga incepe corpul fiecarei functii. O acolada dreapta corespunzatoare trebuie sa fie la sfarsitul functiei.

4. printf()

Sistemul C contine o biblioteca standard de functii care poate fi utilizata in programe. "printf()" este o functie din biblioteca care tipareste pe ecran. Aceasta este o functie descrisa in biblioteca "stdio.h" (care se numeste prototipul functiei "printf()").

5. "azi am inceput laboratoarele de C\n"

Un sir constant in C consta dintr-un numar de caractere incadrate intre ghilimele. Acest sir este un argument al functiei "printf()". Caracterele \n de la sfarsitul sirului (se citesc "backslash n") reprezinta, de fapt, un singur caracter numit "newline".

O forma echivalenta a programului de mai sus:

#include <stdio.h> main()

{

printf("azi am inceput "); printf("laboratoarele de C\n");

}

--------------

Observatii:

--------------

1. Primul "printf" contine la sfarsit un spatiu.

--------------------------------------

Variabile, expresii si asignari

--------------------------------------

In urmatorul exemplu vom ilustra folosirea variabilelor pentru manipularea valorilor intregi.

Variabilele sunt folosite sa memoreze valori. Din moment ce diferite tipuri de variabile sunt folosite

sa memoreze diferite tipuri de date, tipul fiecarei variabile trebuie specificat.

Pentru a ilustra aceasta idee vom calcula cate ore si minute contin un anumit numar de zile. Algoritmul ar fi:

1. asigneaza un numar de zile unei variabile;

2. calculeaza numarul de ore si memoreaza-l intr-o variabila;

3. calculeaza numarul de minute si memoreaza-l intr-o variabila;

4. afiseaza numarul de zile, ore si minute pe ecran.

Urmatorul program scris in C reprezinta implementarea algoritmului precedent:

#include <stdio.h> main()

{

int zile, ore, minute;

zile=7;

ore=24*zile;

minute=60*ore;

printf("O saptamana are %d ore, %d minute.\n",ore, minute);

}

Cand compilam si rulam acest program, pe ecran va apare mesajul:

O saptamana are 168 ore, 10080 minute.

-------------

Explicatii:

-------------

1. Linia

int zile, ore, minute; reprezinta o declaratie de variabile. Variabilele zile, ore, minute sunt declarate de tip "int", unul dintre cele mai importante tipuri din C. O variabila de tip "int" poate lua o valoare intreaga intre -32678 si 32677. Toate variabilele dintr-un program trebuie declarate inainte de a fi utilizate. Declaratiile, la fel ca si instructiunile, au la sfarsit ";".

2. Linia

zile=7;

reprezinta o instructiune de atribuire (sau asignare). Semnul "=" este operatorul de asignare de baza in C. Valoarea expresiei din partea dreapta a simbolului "=" este atribuita variabilei din partea stanga.

3. Instructiunea

printf("O saptamana are %d ore, %d minute.\n",ore, minute); este similara celei prezentate in exemplul precedent, dar are trei argumente. Primul argument, intotdeauna un sir de caractere, se numeste sir de control. Aici, intalnim specificarea de conversie (care se mai numeste format) "%d". Formatele "%d" determina tiparirea valorilor expresiilor corespunzatoare (al doilea si al treilea argument) in formatul intregilor zecimali. Asadar, primul format "%d" corespunde cu valoarea variabilei "ore", iar cel de-al doilea format "%d" cu valoarea variabilei "minute".

In C, toate variabilele trebuie declarate inainte de a fi utilizate in expresii si instructiuni. Forma generala a unui program simplu este:

directive de precompilare

main()

{

declaratii

instructiuni

}

Un nume de variabila, numit si identificator, consta dintr-o secventa de litere, cifre si "underscore", dar fara a incepe cu cifra. Cuvintele cheie, numite si cuvinte rezervate, nu pot fi utilizate ca nume de variabile. Exemple de cuvinte cheie: char, int, float.

Operatorii binari

+

-

*

/

%

sunt folositi pentru adunare, scadere, inmultire, impartire sau modul.

Exemple: 5 % 2 = 1 si 7 % 4 = 3. Evident, in expresia a % b, b nu trebuie sa fie zero, din moment ce nu se poate face impartirea cu

zero.

----------------------------------------------------------------

Un exemplu de utilizare a variabilelor de tip float

----------------------------------------------------------------

#include <stdio.h>

main()

{

float x, y;

x = 1.0;

y = 2.0;

printf("Suma dintre x si y este %f.\n", x+y);

}

Pe ecran se va afisa

Suma dintre x si y este 3.000000.

--------------

Observatii:

--------------

1. Linia

float x, y; semnifica declararea variabilelor x si y de tip "float" (deci de tip real). In realitate sunt numere rationale din intervalul [-10^{308},-10^{-308}] U [10^{-308},10^{308}]

2. Linia printf("Suma dintre x si y este %f.\n", x+y); are doua argumente si reprezinta o tiparire la ecran. De remarcat, ca spre deosebire de exemplele

precedente (unde foloseam formatul %d), aici folosim formatul pentru numere reale, care este "%f".

3. Precizarea tipului variabilelor este esential. De exemplu, daca, sa zicem, ca x=7.0 si y=2.0, sunt

declarati ca fiind de tip float, atunci x/y se evalueaza la 3.5. Daca, insa x=7 si y=2, sunt declarati ca fiind de tip int, atunci x/y se evalueaza la 3 (ramane doar partea intreaga).

----------------

Initializarea

----------------

Initializarea variabilelor se poate face si cand se declara acestea. De exemplu, putem scrie:

char c='A'; int i=1; Putem astfel modifica programul precedent, inlocuind liniile int zile, ore, minute;

zile=7;

cu

int zile=7, ore, minute;

--------------

Observatii:

--------------

1. De obicei, pentru initializarea unei variabile se folosesc constante sau expresii constante. Se pot

insa folosi si variabile care au deja valoarea precizata. De exemplu, urmatoarea secventa de program este corecta:

------------------------------------

Folosirea directivei #define

------------------------------------

Reamintim ca in procesul de compilare a programelor C, intai este invocat preprocesorul. De exemplu, pot fi incluse fisiere, sau anumite siruri de caractere specificate pot fi modificate in alte

siruri. Directivele de preprocesare incep cu caracterul # (in C traditional, acesta trebuie pus pe prima coloana, pe cand in ANSI C poate fi precedat de spatii). Se recomanda scrierea # pe prima coloana, iar a directivelor de precompilare la inceputul programului. Iata cateva exemple de folosire a directivei "#define":

#define LIMIT 100

#define PI

3.14159

--------------

Observatii:

--------------

1. Daca aceste directive de preprocesare apar la inceputul fisierului, atunci in momentul compilarii

preprocesorul schimba toate aparitiile identificatorului LIMIT la 100 si a lui PI cu 3.14159.

Singurele care raman neschimbate sunt sirurile constante. De exemplu, preprocesorul va schimba

printf("PI = %f\n", PI);

Deoarece identificatorul PI se va inlocui peste tot (cu exceptia sirurilor constante) in 3.14159, atunci acesta se va numi constanta simbolica.

2. Directiva #define poate aparea oriunde in program, dar ea afecteaza numai liniile care urmeaza

acesteia.

3. Prin conventie, identificatorii care trebuie schimbati de preprocesor se scriu cu majuscule.

--------------------------------------------------

Avantaje ale folosirii directivei #define

in

printf("PI = %f\n", 3.14159);

--------------------------------------------------

1. Lizibilitate marita. Se refera la citirea si intelegerea rapida a fisierului sursa (PI stim ce inseamna

si ne amintim ca este 3.ceva, deci nu trebuie sa scriem de fiecare data valoarea sa);

2. Schimbarile ulterioare ale unor valori constante se face foarte usor. De exemplu, vrem sa

modificam valoarea lui LIMIT la 10000. In locul liniei

#define LIMIT 100 scriem

#define LIMIT 10000 Daca nu am fi folosit acest mod de definire a constantei LIMIT, atunci ar fi trebuit sa modificam peste tot in program 100 cu 10000.

-----------------------------------------------

Folosirea functiilor printf() si scanf()

-----------------------------------------------

Functiile de tiparire, respectiv de citire, "printf()" si "scanf()" au urmatoarele argumente:

sir_de_control

si

celelalte_argumente

unde sir_de_control este un sir care poate contine specificatii de conversie, sau formate. O specificare de conversie incepe cu caracterul % si se termina cu caracterul de conversie. De exemplu, in formatul %d, litera "d" este caracterul de conversie.

---------------------------

Folosirea lui printf()

---------------------------

Reamintim ca formatul %d este folosit pentru scrierea valorii unei expresii ca un intreg zecimal. In mod similar:

- %c este folosit pentru tiparirea unei expresii ca un caracter

- %f este folosit pentru tiparirea unei expresii reale

- %s este folosit pentru tiparirea unui sir de caractere

-----------

Exemplu:: Fie instructiunea:

-----------

printf("Multime de argumente: %s %d %f %c%c\n","one",2,2.33,'G','O');

Argumentele lui "printf()" sunt separate de virgula, deci avem sase argumente. Primul argument este sirul de control. Obtinem corespondenta:

%s <---> "one" %d <---> 2 %f <---> 2.33 %c <---> 'G' %c <---> 'O'

Cand se executa programul ce contine aceasta instructiune, obtinem:

Multime de argumente: one 2 2.330000 GO

--------------

Observatii:

--------------

1. Daca instructiunea "printf()" contine prea multe caractere, atunci se poate scrie aceasta pe mai multe linii, separate prin virgula. De exemplu, putem scrie:

printf("%s%s\n", "Aceasta instructiune se va scrie ", "pe o linie de text foarte lunga.\n"); Tabelul de mai jos descrie cum caracterele de conversie afecteaza argumentele corespunzatoare. printf() |--------------------------------------------------------------------------------|

| Caracter

|

|

| de

| Cum este tiparit argumentul corespunzator ?

|

| conversie |

|

|--------------------------------------------------------------------------------|

| ca un caracter

c

|

|

| ca un intreg zecimal

d

|

 

|

| ca un numar in virgula flotanta in notatia stiintifica |

e

|

 

| ca un numar in virgula flotanta

f

|

 

|

| in format e sau f (alegand cel mai scurt dintre ele)

g

|

|

| ca un sir

s

|

 

|

|---------------------------------------------------------------------------------| Cand un argument este tiparit, locul unde este tiparit se numeste campul sau, iar numarul de caractere ale acestui camp se numeste lungimea campului. Aceasta lungime poate fi specificata intr- un format ca un intreg plasat intre caracterul % si caracterul de conversie. De exemplu, instructiunea printf("%c%3c%7c\n", 'A', 'B', 'C');

va tipari

A

B

C

Pentru numerele in virgula flotanta, putem controla precizia (numarul de cifre zecimale), la fel ca

lungimea campului. Forma generala a formatului este %m.nf si semnifica ca m este lungimea campului, iar n precizia. Formatul %mf specifica doar lungimea campului, iar formatul %.nf numai precizia. De exemplu, instructiunile:

printf("Numere1: %.1f %.2f %.3f\n", 1.0, 2.0, 3.0); printf("Numere2: %7.1f %7.2f %7.3f\n", 4.0, 5.0, 6.0);

vor avea ca efect afisarea:

Numere1: 1.0 2.00 3.000 Numere2: 4.0 5.00 6.000

--------------------------

Folosirea lui scanf()

--------------------------

Functia "scanf()" este asemanatoare cu "printf()", dar este folosita pentru intrari in loc de iesiri. Primul sau argument este un sir de control care are formatele corespunzatoare cu variatele moduri de interpretare a sirurilor de intrare. Dupa sirul de control urmeaza adresele variabilelor. Adresa unei variabile este locul din memorie unde este memorata variabila (vom reveni in capitolele

viitoare). Simbolul "&" reprezinta operatorul de adresa. De exemplu, scanf("%d", &x); formatul %d implica interpretarea caracterelor tiparite la intrare ca un intreg zecimal, si apoi memorarea valorii variabilei la adresa lui x. Tabelul de mai jos descrie efectele caracterelor de conversie din formate folosite de functia scanf(). scanf() |-------------------------------------------------------------------------|

|

Caracter

|

|

| de

|

La ce caractere se face convertirea ?

|

| conversie |

 

|

|-------------------------------------------------------------------------|

| la un caracter

c

|

|

| la un intreg zecimal

d

|

 

|

| la un numar in virgula flotanta (float)

f

|

 

|

|

lf

|

 

la un numar in virgula flotanta (double)

|

| la un numar in virgula flotanta (long double)

Lf

|

 

|

| s

|

la un sir

|

|-------------------------------------------------------------------------|

-----------

Atentie ! Functia "prinf()" foloseste formatul %f pentru tiparirea numerelor

-----------

float si double, pe cand "scanf()" foloseste formatul %f pentru citirea unui float si %lf pentru citirea unui double.

------------

Exemplu:: Aria unui cerc.

------------

#include <stdio.h>

#define PI 3.141592653589793 main()

{

double raza; printf("\n%s\n\n%s", "Acest program calculeaza aria cercului", "Dati raza:"); scanf("%lf", &raza);

printf("\n%s\n%s%.2f%s%.2f%s%.2f\n%s%.5f\n\n",

"Aria = PI * raza * raza",

" = ", PI, " * ", raza, " * ", raza,

" = ", PI * raza * raza);

}

Presupunem ca la executia programului introducem raza egala cu 2.333. Atunci vor apare pe ecran:

Acest program calculeaza aria cercului

Dati raza: 2.333

Aria = PI * raza * raza

= 3.14 * 2.33 * 2.33

= 17.09934

Daca am calcula separat (pe hartie), am obtine Aria = 3.14 * 2.33 * 2.33 = 17.046746, numar care nu coincide cu cel furnizat de calculator. Justificarea este aceea ca PI si raza sunt tiparite doar cu doua zecimale, pe cand valorile lor sunt pastrate in memorie cu precizie mai mare.

-----------------------------

Instructiunea "while"

-----------------------------

Instructiunea while face parte din categoria actiunilor repetitive. Pentru a intelege aceasta

instructiune, vom face un exemplu de adunare a numerelor de la 1 la 10.

-------------

Exemplu::

-------------

#include <stdio.h>

main()

{

int i=1, suma=0;

while (i<=10)

{

suma = suma + i; i = i + 1;

}

printf("Suma primelor 10 numere este %d\n",suma);

}

-------------

Explicatii:

-------------

1. La linia

int i=1, suma=0; se declara variabilele i si sum de tip int si sunt initializate cu 1 si 0, respectiv.

2. Constructia

while (i<=10)

{

suma = suma + i; i = i + 1;

}

reprezinta o instructiune while (sau iteratie while). Mai intai, se evalueaza expresia i<=10. Cum valoarea initiala a lui i este egala cu 1, rezulta ca se vor executa instructiunile dintre acolade. Astfel, variabila suma va fi asignata cu vechea valoare a lui suma la care se adauga valoarea lui i. Deci, suma se evalueaza la 1. Apoi, variabila I se evalueaza la suma dintre vechea valoare a lui i (i=1) si 1, deci este egala cu 2. In acest moment, executia revine la inceput adica evaluam expresia i<=10. Cum valoarea lui i este 2, rezulta ca se va executa iar corpul lui while. La sfarsitul acestei iteratii, suma este evaluata la 1+2, iar i la 3. Se observa usor ca i<=10 este tot adevarata, deci se va executa din nou corpul lui while. La sfarsitul celei de-a treia iteratii, sum este evaluata la 1+2+3, iar i la 4.

Procesul continua pana cand valoarea lui i este 11, care implica falsitatea expresiei i<=10. Astfel se iese din bucla while.

3. Instructiunea

printf("Suma primelor 10 numere este %d\n", suma); va afisa mesajul Suma primelor 10 numere este 55.

4. Formatul general al instructiunii while este

while (expresie) instructiune unde instructiune poate fi o singura instructiune sau un grup de instructiuni delimitate prin { si } (care se mai numeste si instructiune compusa).

-----------------------------------------------

Generalizarea problemei precedente

-----------------------------------------------

Dorim sa citim mai multe numere (fara a sti aprioric numarul lor si care sunt acestea) si dorim sa afisam suma lor.

Consideram urmatorul algoritm cu patru pasi:

1. Initializarea a doua variabile contor si suma;

2. Afisarea unor mesaje utile citirii numerelor;

3. Citirea repetata a numerelor, incrementarea variabilei contor si adunarea numarului citit la suma;

4. Afisarea celor doua valori ale variabilelor.

Instructiunea while este una din cele trei constructii existente in C menite sa descrie actiuni repetitive. In solutia noastra, utilizam valoarea returnata de functia "scanf()" pentru a controla instructiunea while. Consideram urmatorul cod C:

#include <stdio.h> main()

{

int contor = 0; float suma = 0.0, x;

printf("Suma urmatoarelor numere va fi calculata\n"); printf("Dati numerele:\n"); while (scanf("%f", &x)==1)

{

contor = contor +1;

suma = suma +x;

}

printf("\n%s%5d\n%s%12f\n\n",

"Numarul de numere: ", contor, "Suma lor: ", suma);

}

--------------

Explicatii:

--------------

1. scanf("%f", &x)==1

Simbolul == reprezinta operatorul de egalitate. Expresia a==b intoarce true daca valoarea lui a este egala cu valoarea lui b. De exemplu, 1==1 intoarce true, 2==3 intoarce false. Functia "scanf()" are rolul de a citi caractere scrise de utilizator, sa le converteasca la float, si sa plaseze aceasta valoare la adresa lui x. Daca totul se desfasoara cu succes, atunci scanf() intoarce valoarea 1, adica true. Daca din anumite motive, procesul de conversie esueaza, atunci se intoarce valoarea 0 (deci false). Daca nu mai introducem nici o data (Control^z in MD-DOS, CR urmat de Control^d in UNIX), atunci scanf() va intoarce valoarea -1 (deci tot false).

2.

while (scanf("%f", &x)==1)

{

contor = contor +1; suma = suma +x;

}

Dupa cum am vazut mai sus, bucla while se executa atata timp cat scanf("%f", &x)==1 se evalueaza

la true. Iteratia se incheie cand tastam Control^z sau ceva ce nu se poate converti la float, de exemplu 'a' sau CR. La fiecare executie a buclei se incrementeza variabila contor, iar valoarea variabilei suma creste cu valoarea lui x (citita de la tastatura).

3. printf("\n%s%5d\n%s%12f\n\n",

"Numarul de numere: ", contor, "Suma lor: ", suma); Presupunem ca executam acest program pentru numerele 1.1 2.02 3.003 4.0004 5.00005 Pe ecran va apare rezultatul:

Numarul de numere: 5 Suma lor: 15.123449

---------------

Observatii:

---------------

1. Daca numaram spatiile, observam ca valoarea lui contor a fost tiparita pe un camp de 5 caractere,

iar suma pe 12 caractere. Aceasta este cauzata de formatele "%5d" si "%12f". Retineti ca tiparirea zecimalelor pentru suma este gresita de la a treia zecimala.

---------------------------------------------

Stilul de redactare al programelor

---------------------------------------------

Un stil bun de scriere a codului este esential pentru arta programarii. Aceasta faciliteaza citirea, scrierea si intretinerea programelor. Un stil bun foloseste:

1. spatii goale si comentarii, astfel incat codul este usor de citit si de inteles;

2. utilizarea indentarii este cruciala, care indica cu precizie structurile de control. De exemplu, in constructia while (expresie) instructiune

indentarea instructiunii indica ca executia acesteia este sub controlul iteratiei while;

3. alegerea de nume sugestive pentru variabile;

4. corespondenta dintre acolade. De exemplu, urmatorul program este scris in stilul "Bell Labs

industrial programming style" (#,{,},m pe prima coloana).

#include <stdio.h> #include <stdlib.h>

#define GO "Start"

main()

{

}

---------------

Observatii:

---------------

programelor. Atentie ! Utilizati strategia care este deja in uz.

-----------------------------------------

Erori de programare frecvente

-----------------------------------------

Mai ales la inceputul invatarii programarii pe calculator, se fac multe erori simple, cum ar fi:

1. neinchiderea sirului cu ghilimele drepte. Cand compilatorul intalneste ghilimeaua stanga, atunci

incepe sa colecteze toate caracterele care urmeaza spre a forma un sir de caractere, pana intalneste ghilimeaua dreapta. Daca aceasta nu exista, atunci compilatorul da mesajul de eroare, de tipul:

Unterminated string or character constant

2.

scrierea gresita a numelui variabilelor;

3.

nedeclararea lor.

In

general, compilatorul da un mesaj de eroare potrivit, alteori din cauza interpretarii separate, nu.

De exemplu, scriem gresit "prinf()" in loc de "printf()". Atunci compilatorul va da un mesaj prin

care ne informeaza ca nu gaseste functia "prinf()". Deci, el nu isi da seama de omiterea unei litere,

ci

o interpreteaza in mod diferit.

4.

daca dorim sa citim o variabila de tip double, atunci in functia "scanf()" trebuie sa specificam

formatul "%lf";

5. faptul ca uneori uitam ca in formatul "%m.nf" pentru afisarea cu virgula mobila, m semnifica

lungimea campului. De exemplu, pentru a specifica doua zecimale la dreapta si trei la stanga, nu

trebuie sa scriem %2.3f, ci %6.3f deoarece se numara si punctul zecimal;

6. poate cea mai frecventa greseala cand se foloseste scanf() este uitarea operatorului de adresa.

Daca, de exemplu, scriem scanf("%d%d", a, b) in loc de scanf("%d%d", &a, &b) atunci compilatorul nu se va "prinde" de eroare. Bineinteles, ca vom obtine rezultate eronate (run- time error) care sunt ceva mai greu de corectat.

---------------------------------------

Redirectarea intrarii si iesirii

---------------------------------------

Putem citi valorile variabilelor dintr-un fisier, nu neaparat de la tastatura. De exemplu, fisierul nostru executabil se numeste "ex1.exe". Pentru a extrage datele din fisierul de intrare "in.dat", dam comanda

ex1.exe < in.dat Putem, de asemenea, sa scriem valorile unor variabile intr-un fisier extern, sa zicem "out.dat". Astfel dam comanda ex1.exe > out.dat Evident, daca se doreste atat citirea unor variabile din fisier, cat si scrierea rezultatelor in alt fisier, atunci se va da comanda ex1.exe < in.dat > out.dat

-------------

Exemplu::

-------------

#include <stdio.h>

main()

{

char ch;

while (scanf("%c", &ch) ==1)

{

printf("%c", ch);

printf("%c", ch);

}

}

-----------------------------------------------

Exercitii propuse spre implementare

-----------------------------------------------

1. Presupunem ca dispunem de rezultatele din anul curent relative la cursul de schimb valutar dintre

leu, dolar, marca si franc. Sa se scrie un program C care deseneaza (cu caractere ASCII) graficul

evolutiei raportului leu/valuta, unde valuta={dolar, marca, franc}. Apoi, desenati graficul raportului dolar/marca si marca/franc.

2. Scrieti un program C care primeste la intrare un numar de secunde, si intoarce numar maxim de

ore, de minute, de secunde care este echivalent ca timp. De exemplu, 7384 secunde este echivalent cu 2 ore, 3 minute si 4 secunde.

3. Folosind o bucla while, scrieti un program C care calculeaza al n-lea termen din sirul din

Fibonacci. Reamintim ca sirul lui Fibonacci este dat de recurenta liniara de ordin 2:

a_1=a_2=1;

a_{n+2}=a_{n+1}+a_n, n>=1

Atomi lexicali, operatori, sistemul C

2.1. Caractere si atomi lexicali

2.2. Avantajele folosirii comentariilor

2.3. Cuvinte rezervate

2.4. Identificatori

2.5. Constante

2.6. Siruri constante

2.7. Operatori si semne de punctuatie

2.8. Operatorii de precedenta si asociativitate

2.9. Operatorii de incrementare si decrementare

2.10. Operatori de asignare

2.11. Sistemul C

2.12. Exercitii propuse spre implementare

========= Capitolul 2 ========= =========================== Atomi lexicali, operatori, sistemul C ===========================

Ca si alte limbaje, C are un alfabet si reguli pentru scrierea programelor corecte folosind semne de punctuatie. Aceste reguli formeaza sintaxa limbajului C. Compilatorul C are rolul de a testa daca un

program C este corect. Daca sunt erori, atunci va afisa o lista de mesaje de eroare si se va opri. Daca nu sunt erori, atunci compilatorul va "traduce" acest program in cod obiect, folosit de incarcator pentru producerea codului executabil. Mai intai compilatorul imparte multimea caracterelor (programul sursa) in atomi lexicali, care reprezinta vocabularul de baza al limbajului. In ANSI C (ANSI = American National Standards Institute) sunt sase tipuri de atomi lexicali (care se mai numesc si elemente lexicale sau unitati lexicale):

1. cuvinte rezervate (sau cheie);

2. identificatori;

3. constante;

4. siruri constante;

5. operatori;

6. semne de punctuatie.

----------------------------------

Caractere si atomi lexicali

----------------------------------

In fapt, un program C este o secventa de caractere. Caracterele permise in programele C sunt:

1. litere mici

: a b

z

2. litere mari

: A B

Z

3. cifre

: 0 1

9

4. alte caractere: d f * / = ( ) { } [ ] < > ' " ! @ # $ % & _ | ^ ~ \ . , ; : ?

---------------

Comentarii

---------------

Comentariile sunt siruri de caractere cuprinse intre /* si */. Comentariile nu reprezinta atomi

lexicali. Compilatorul va traduce comentariile intr-un singur caracter spatiu, de aceea comentariile nu fac parte din codul executabil.

-----------

Atentie ! Pentru a verifica aceasta, puteti citi lungimea unui cod executabil (fara comentarii) si ----------- apoi sa comparati lungimea codului executabil obtinut dupa o noua compilare (cu

comentarii).

-------------------------------

Exemple: de comentarii:

-------------------------------

1. /* un comentariu */

2. /** al doilea comentariu **/

3. /*****/

4. /*

*

Al patrulea

*

comentariu

*/

5. /**************

* Al cincilea *

* comentariu *

**************/

--------------------------------------------

Avantajele folosirii comentariilor:

--------------------------------------------

1. Principalul scop este usurarea documentarii ulterioare. Scopul documentarii este explicarea clara

a folosirii programelor;

2. Uneori un comentariu poate contine informatii ce argumenteaza demonstratia corectitudinii

acelui algoritm;

3. Sfat ! Folositi comentariile in timpul introducerii textului programului.

-----------------------

Cuvinte rezervate

-----------------------

Cuvintele rezervate (cheie) au un inteles strict insemnand un atom individual. Ele nu pot fi redefinite sau utilizate in alte contexte. Iata lista lor:

auto

do

goto

signed

unsigned

break

double

if

sizeof

void

case

else

int

static

volatile

char

enum

long

struct

while

const

extern

register switch

continue float

return

typedef

default

for

short

union

Anumite implementari pot contine si alte cuvinte rezervate:

asm

cdecl

far

huge

interrupt

near

pascal

Comparativ cu alte limbaje de programare, C are un numar mic de cuvinte rezervate. Ada, de exemplu, are 62 cuvinte rezervate. Aceasta este o caracteristica a limbajului C de a avea doar cateva

simboluri speciale si cuvinte rezervate.

-----------------

Identificatori

-----------------

Un identificator este un atom lexical compus din secventa de litere, cifre sau underscore ("_") cu restrictia ca primul caracter este o litera sau underscore. In multe implementari C, se face distinctie dintre litere mici si mari. In general, se obisnuieste ca identificatorii sa fie scrisi cu nume sugestive care sa usureze citirea si documentarea programului.

------------

Exemple::

------------

1. k, _id, contor, un_identificator sunt identificatori;

2. gresit#unu, 100_gresit_doi, -plus nu sunt identificatori.

Identificatorii sunt creati pentru a da nume unice pentru diverse obiecte dintr-un program.

Cuvintele rezervate pot fi privite ca fiind identificatori. Identificatori precum "printf()" sau "scanf()" sunt deja cunoscuti sistemului C ca fiind functii de intrare/iesire.

O diferenta majora dintre sistemele de operare si sistemele C o reprezinta lungimea admisa pentru

numele identificatorilor. Astfel, pentru unele sisteme vechi, este acceptat un identificator al carui nume are mai mult de 8 caractere, dar numai primele 8 sunt semnificative. De exemplu, identificatorul _23456781 este privit la fel ca _23456782.

In ANSI C, primele 31 de caractere sunt luate in considerare.

---------

Atentie !

---------

Identificatorii care incep cu underscore pot fi confundati cu numele variabilelor sistem. De exemplu, identificatorul _iob declarat in biblioteca <stdio.h> este folosit pentru numele unui vector de structuri. Daca un programator foloseste un identificator cu acelasi nume, dar pentru alte scopuri, atunci ori se va semnala o eroare aparent necunoscuta, ori (si mai rau) compilatorul se va comporta ciudat. Recomandarea este: Nu folositi identificatori care incep cu underscore.

-------------

Constante

-------------

C manipuleaza diferite tipuri de valori. Numere precum 0 si 17 sunt exemple de constante intregi,

iar numere precum 1.0 si 3.14159 sunt exemple de constante numere zecimale. Ca si multe alte limbaje, C trateaza constantele "int" si "float" in mod diferit. Constantele caracter sunt foarte apropiate de tipul "int" (vom reveni). Un caracter special l-am si intalnit deja. Este vorba de '\n', care se mai cheama "secventa escape". In traducere libera, ar insemna "evadare a lui n din intelesul uzual". In fapt, el este folosit pentru a trece cursorul curent la linie noua (newline). Constantele de intregi, reali, caractere si enumerare sunt toate colectate de compilator ca fiind atomi lexicali. Din cauza limitelor impuse de memoria masinilor, unele constante care pot fi exprimate sintactic nu pot fi disponibile pe o masina anume. De exemplu, numarul

123456789000000000000 nu poate fi memorat ca fiind un intreg.

---------------------

Siruri constante

---------------------

O secventa de caractere incadrate intre ghilimele, de exemplu "abc", este un sir constant. Este

inteles de compilator ca fiind un singur atom lexical. In capitolele ulterioare, vom vedea ca de fapt sirurile constante se memoreaza ca siruri de caractere. Sirurile constante sunt tratate mereu diferit fata de constantele de tip caracter. De exemplu, "a" nu este totuna cu 'a'. De mentionat ca ghilimeaua " reprezinta un singur caracter, nu doua. De aceea, daca dorim sa apara intr-un sir constant, atunci ea trebuie precedata de \ (backslash). Daca dorim ca intr-un sir sa apara \, atunci trebuie sa-l precedam tot cu \ (devenind astfel \\).

------------

Exemple::

------------

1. "sir text"
2. ""
3. "

4. " a = b + c "

"

/* sirul vid */ /* sir de spatii */ /* nu se executa nimic */

5. " /* acesta nu este un comantariu */ "

6. " un sir ce contine ghilimea \" "

7. " un sir ce contine backslash \\ "

8. /* "gresit" */

9. "gresit doi"

/* nu este un sir */

/* nici asta nu este sir */

Doua siruri constante care sunt separate doar printr-un spatiu vor fi concatenate de compilator intr- unul singur. De exemplu, "abc" "def" este echivalent cu "abcdef" Aceasta este o trasatura a limbajului ANSI C, nefiind disponibil in C traditional. Sirurile constante sunt tratate de compilator ca atomi lexicali. Ca si alte constante, compilatorul va rezerva spatiu in memorie pentru pastrarea sirurilor constante.

-------------------------------------------

Operatori si semne de punctuatie

-------------------------------------------

In C, exista multe caractere speciale cu inteles specific. De exemplu, operatorii aritmetici

+

-

*

/

%

reprezinta adunarea, scaderea, inmultirea, impartirea, modulul, respectiv. Reamintim (pentru

bubulici) ca a % b inseamna restul impartirii intregi a lui a la b (notatie matematica: a mod b; a nu se confunda modul cu valoarea absoluta). De exemplu, 5 % 3 are valoarea 2. Atentie la numere intregi negative (Vezi Exercitiul 1). Anumite simboluri au intelesuri dependente de context. Consideram simbolul % din instructiunile

printf("%d", a);

si

a = b % 7;

Primul simbol % este un format de scriere, pe cand al doilea reprezinta operatorul modul.

In exemplul de mai jos, parantezele (,) se folosesc atat pentru a preciza ca () este un operator

("main" reprezinta numele unei functii), cat si ca semne de punctuatie.

main()

{

int a, b = 2, c = 3; a = 17 * (b + c);

}

Anumite caractere speciale sunt folosite in multe contexte. Fie espresiile

a + b

++a

a += b

Ele folosesc caracterul +, dar ++ este un singur operator, la fel ca si +=.

------------------------------------------------------

Operatorii de precedenta si asociativitate

------------------------------------------------------

Operatorii au reguli de precedenta si asociativitate care implica evaluarea expresiilor. Din moment ce expresiile din interiorul parantezelor se evalueaza mai intai, este clar ca parantezele sunt folosite pentru a preciza care operatii se fac mai intai. Consideram expresia

1 + 2 * 3

In C, operatorul * are prioritate (precedenta) mai mare decat +, deci se va face intai inmultirea apoi adunarea. Deci valoarea expresiei este 7. O expresie echivalenta este

1 + (2 * 3)

Pe de alta parte, expresia (1 + 2) *3 este diferita; ea are valoarea 9. Consideram acum expresia 1 + 2 - 3 + 4 - 5. Operatorii + si - au aceeasi precedenta, deci se va

folosi regula de asociativitate la stanga. Astfel (((1 + 2) - 3) + 4) -5 este o expresie echivalenta. In continuare vom prezenta un tabel in care precizam regulile de precedenta si asociativitate pentru cativa operatori din C. |----------------------------------------------|----------------------------|

| Operatori

|----------------------------------------------|----------------------------|

| () ++ (postfix) -- (postfix)

|----------------------------------------------|----------------------------|

| +(unar) -(unar) ++(prefix) --(prefix) | de la dreapta la stanga | |----------------------------------------------|----------------------------| *

|

|----------------------------------------------|----------------------------|

|

|----------------------------------------------|----------------------------|

| =

|

Asociativitate

|

| de la stanga la dreapta |

/

%

-

*=

| de la stanga la dreapta |

| de la stanga la dreapta |

| de la dreapta la stanga |

+

+=

-=

/=

etc.

|----------------------------------------------|----------------------------| Toti operatorii de pe o linie (de exemplu, *, /, %) au aceeasi prioritate intre ei, dar au prioritate mai mare decat cei ce apar in liniile de mai jos. Operatorii + si - pot fi si binari si unari. De remarcat ca cel unar are prioritate mai mare. De exemplu, in expresia - a * b - c primul operator - este unar, pe cand al doilea binar. Folosind regulile de precedenta, se vede ca aceasta este echivalenta cu ((- a) * b) - c

----------------------------------------------------------

Operatorii de incrementare si decrementare

---------------------------------------------------------

Operatorii de incrementare si de decrementare (++, --) au o prioritate foarte mare (dupa cum se poate vedea in tabelul de mai sus) si se pot asocia atat de la dreapta la stanga, cat se de la stanga la

dreapta. Operatorii ++ si -- se pot aplica variabilelor, dar nu si constantelor. Mai mult, ei pot apare ca notatie prefixata, cat si postfixata. De exemplu, putem avea ++i si contor++, dar nu putem avea 167++ sau ++(a * b - 1). Fiecare din expresiile ++i si i++ au o valoare; mai mult fiecare cauzeaza incrementarea valorii variabilei i cu o unitate. Diferenta este:

1. expresia ++i va implica intai incrementarea lui i, dupa care expresia va fi evaluata la noua valoare a lui i;

2. expresia i++ va implica evaluarea sa la valoarea lui i, dupa care se va incrementa i.

------------

Exemplu::

------------

int a, b, c = 0;

a

b

printf("a=%d b=%d c=%d ++c=%d\n", a, b, c, ++c);

= ++c;

= c++;

Intrebare: Ce se va tipari la ecran ? Intr-un mod similar, --i va implica decrementarea valorii lui i cu 1, dupa care expresia --i va avea noua valoare a lui i, pe cand i-- se va evalua la valoarea lui i, dupa care i se va decrementa cu 1. Retineti deci ca, spre deosebire de + si -, operatorii ++ si -- vor determina schimbarea valorii variabilei i din memorie. Se mai spune ca operatorii ++ si -- au efect lateral (side effect). Daca nu folosim valoarea lui ++i sau a lui i++, atunci acestea sunt echivalente. Mai precis,

++i;

si

i++;

sunt echivalente cu

 

i

= i + 1;

------------

Exemple::

------------

Presupunem ca avem declaratiile int a = 1, b = 2, c = 3, d = 4;

Atunci avem:

Expresie

Expresie echivalenta parantetizata

Valoare

a

* b / c

(a * b) / c

0

a * b % c + 1

((a * b) % c) + 1

3

++ a * b - c --

((++ a) * b) - (c --)

1

7 - - b * ++ d

7 - ((- b) * (++ d))

17

-----------------------------

Operatori de asignare

----------------------------

Pentru schimbarea valorii unei variabile, am utilizat deja instructiunea de asignare (atribuire), cum ar fi

a = b + c;

Spre deosebire de celelalte limbaje, C trateaza = ca un operator. Precedenta sa este cea mai mica dintre toti operatorii si asociativitatea sa este de la dreapta la stanga. O expresie de asignare simpla are forma:

variabila = parte_dreapta unde "parte_dreapta" este o expresie. Daca punem ; la sfarsitul expresiei de asignare, atunci vom obtine instructiune de asignare. Operatorul = are doua argumente, "variabila" si "parte_dreapta". Valoarea expresiei "parte_dreapta" este asignata pentru "variabila" si aceasta valoare se returneaza de catre expresia de asignare (ca un tot unitar).

------------

Exemplu:: Consideram instructiunile

------------

b = 2;

c = 3;

a = b + c;

unde toate variabilele sunt de tipul int. Folosind faptul ca = este un operator, putem condensa aceasta la

a = (b = 2) + (c = 3);

Explicatia este ca expresia de asignare b = 2 atribuie valoarea 2 atat variabilei b, cat si instructiunii intregi. Daca exemplul de mai sus pare artificial, atunci o situatie frecvent intalnita este asignarea multipla.

De exemplu, instructiunea

a = b = c = 0;

este echivalenta cu (folosind asociativitatea de la dreapta la stanga)

a = (b = (c = 0));

Relativ la =, mai exista inca doi operatori. Este vorba de += si -=. Expresia

k = k + 2

va aduna 2 la vechea valoare a lui k si va asigna rezultatul lui k si intregii expresii. Expresia

k += 2

face acelasi lucru.

----------------------------------------

Lista operatorilor de asignare: = += -= *= /= %= >>= <<= &= ^= |=

----------------------------------------

Toti acesti operatori au aceeasi precedenta si se asociaza de la dreapta la stanga. Semantica lor este

specificata de variabila op= expresie care este echivalent cu variabila = variabila op (expresie) cu exceptia faptului ca variabila sa nu fie o expresie.

------------

Exemplu::

------------

Expresia de asignare

j *= k + 3

este echivalenta cu

si nu cu

j

j

= j * (k + 3)

= j * k + 3

Fie declaratia int i = 1, j = 2, k = 3, m = 4;

Consideram urmatoarele exemple de evaluari ale expresiilor

Expresie

i += j + k

j *= k = m + 5

------------

Expresie echivalenta Expresie echivalenta

i += (j + k)

j *= (k = (m + 5))

i = (i + (j + k)) j = (j * (k = (m + 5)))

Exemple:: Calculul puterilor lui 2

-----------

#include <stdio.h>

main()

{

int i = 0, power = 1;

while (++i <= 10)

printf("%6d", power *=2); printf("\n");

}

Iesirea acestui program va fi:

2

4

8

16

---------------

Sistemul C

---------------

32 64

128 256 512 1024

Valoare

6

18

In capitolele precedente am prezentat directiva de preprocesare #include si #define. Directiva #include avea forma generala:

#include <nume_fisier> si insemna includerea in acest loc a fisierului header specificat din directoarele specifice C (MS- DOS \bc\include sau \tc\include, UNIX /usr/include). O alta forma este #include "nume_fisier" ce are drept scop inlocuirea acestei linii cu o copie a fisierului "nume_fisier" din directorul curent. Deci, atunci cand utilizam o functie C, trebuie sa specificam prototipul ei (scanf() si printf() au prototipul <stdio.h>, rand() are prototipul <stdlib.h>).

------------

Exemplu::

------------

#include <stdio.h> #include <stdlib.h>

main()

{

int i, n;

printf("\n%s\n%s", "Vom afisa niste intregi aleatori.", "Cati doriti sa vedeti ? "); scanf("%d", &n); for (i = 0; i < n; ++i)

{

if (i % 6 == 0) printf("\n");

printf("%12d", rand());

}

printf("\n");

}

Daca de exemplu, tastam numarul 11, atunci pe ecran vor apare 11 numere intregi aleatoare.

--------------

Observatii:

--------------

1. Atentie ! ++i < n este diferit de i++ < n;

2. Operatorul == este operatorul de egalitate (test), adica a == b va fi evaluata la true daca si numai

daca valoarea lui a este egala cu valoarea lui b (in caz contrar va fi evaluata la false).

3. Functia rand() intoarce un intreg cuprins intre 0 si n, unde n este dependent de sistem. In ANSI

C, n este dat de constanta RAND_MAX.

-----------------------------------------------

Exercitii propuse spre implementare

-----------------------------------------------

1. Investigati comportarea operatorilor / si % pentru numere intregi negative. Mentionam ca in

unele sisteme C, 7/-2 da rezultatul -3, in altele -4. Verificati daca se pastreaza identitatea din

matematica (prevazuta a fi adevarata de ANSI):

(a / b) * b + a % b = a

Sugestie: Scrieti un program C care sa contina liniile de cod int a, b; printf("dati doi intregi nenuli: "); scanf("%d%d", &a, &b);

printf("%s%4d\n%s%4d\n%s%4d\n%s%4d\n%s%4d\n",

" a =",a,

" b =",b,

" a / b =", a / b,

" a % b =", a % b,

"Verif. ANSI=", (a / b) * b + a % b - a);

2. Scrieti un program C care sa calculeze cel mai mare divizor comun dintre a si b, unde a, b sunt

numere intregi, folosind algoritmul lui Euclid.

3. Din moment ce + si ++ sunt operatori, rezulta ca expresia a+++b poate fi interpretata fie ca

a++ + b

fie

a + ++b

depinzand de modul de grupare semnului +. Scrieti un program scurt pentru a vedea ce interpretare

face compilatorul C.

4. Inlocuiti ++i cu i++ in programul de calcul a puterilor lui 2.

5. Un patrat magic (de latura n) are proprietatea ca include in locatiile sale toate numerele intregi

din intervalul 1,

diagonala sunt egale. De exemplu:

, n^2 si sumele numerelor de pe fiecare linie, fiecare coloana sau fiecare

6

1

8

7

5

3

2

9

4

este un patrat magic de dimensiune 3. Sa se scrie un program C care testeaza daca un patrat este magic sau nu. De asemenea, incercati sa generati toate patratele magice de ordin n.

* n).

3. Controlul instructiunilor

3.1. Operatori relationali, de egalitate si logici

3.2. Operatori si expresii relationale

3.3. Operatori si expresii de egalitate

3.4. Operatori logici si expresii logice

3.5. Evaluare rapida (short-circuit)

3.6. Instructiunea compusa

3.7. Instructiunea vida

3.8. Instructiunile "if" si "if-else"

3.9. Instructiunea "while"

3.10. Instructiunea "for"

3.11. Operatorul ","

3.12. Instructiunea "do"

3.13. Instructiunea "goto"

3.14. Instructiunile "break" si "continue"

3.15. Instructiunea "switch"

3.16. Operatorul conditional

3.17. Exercitii propuse spre implementare

======== Capitolul 3 ======== ================== Controlul instructiunilor ==================

------------------------------------------------------

Operatori relationali, de egalitate si logici

------------------------------------------------------

Operatori relationali : <, >, <=, >= Operatori de egalitate: ==, !=

Operatori logici

Ca si ceilalti operatori, acesti operatori au reguli de precedenta si asociativitate care determina precis modul de evaluare a acestor

expresii.

--------------------------------------------------------------------------------

| Operatori

--------------------------------------------------------------------------------

()

+ (unar) - (unar) ++ (prefix) -- (prefix) | de la dreapta la stanga |

: !, &&, ||

|

Asociativitate

|

++ (postfix)

-- (postfix)

| de la stanga la dreapta |

 

*

/

%

| de la stanga la dreapta |

+

-

| de la stanga la dreapta |

<

<=

>

>=

| de la stanga la dreapta |

==

!=

| de la stanga la dreapta |

&&

| de la stanga la dreapta |

||

| de la stanga la dreapta |

?:

| de la dreapta la stanga |

=

+=

-=

 

*=

/=

etc

| de la dreapta la stanga |

, (operatorul virgula)

| de la stanga la dreapta |

-------------------------------------------------------------------------------

Operatorul ! este unar, spre deosebire de toti operatori (relationali, de egalitate si logici) care sunt binari. Toti operatorii vor fi prezenti in expresii ce pot lua valoarea intreaga 1 sau 0. Motivul este ca

C reprezinta "false" orice expresie egala cu zero, si "true" orice expresie diferita de zero.

------------

Exemple: In continuare, dam o lista de expresii ce se evaluaza la false

------------

1. O expresie de tip int ce are valoarea 0;

2. O expresie de tip float ce are valoarea 0.0;

3. Caracterul null '\0';

4. Pointerul NULL.

-----------------------------------------

Operatori si expresii relationale

-----------------------------------------

Am vazut ca operatorii <, >, <=, >= sunt toti binari. Expresiile ce contin acesti operatori pot lua valoarea 0 sau 1.

-----------

Exemple. Primele patru exemple sunt corecte, restul sunt gresite:

-----------

1. a < 3

2. a > b

3. -1.1 >= (2.2 * x + 3.3)

4. a < b < c

5. a =< b

6. a < = b

7. a >> b

Fie expresia relationala "a < b". Daca valoarea lui a este mai mica decat valoarea lui b, atunci

expresia va avea valoarea 1, pe care o gandim ca fiind "true". Daca valoarea lui a este mai mare decat valoarea lui b, atunci expresia va avea valoarea 0, pe care o gandim ca fiind "false".

Observam ca valoarea lui "a < b" este aceeasi cu valoarea lui "a - b < 0". Folosind precedenta operatorilor aritmetici, aceasta este deci echivalenta cu "(a - b) < 0". De altfel, pe multe masini, expresii cum sunt "a < b" sunt implementate ca fiind "a - b < 0".

-----------

Exemple: Vom considera urmatorul tabel cu declaratii si initializari.

-----------

Presupunem ca avem declaratiile:

(corecta, dar confuza)

int

double x = 5.5, y = 7.7;

i = 1, j = 2, k = 3;

----------------------------------------------------------------------

|

----------------------------------------------------------------------

Expresie

|

Expresie echivalenta

| Valoare |

 

i < j - k

i < (j - k)

0

- i + 5 * j >= k + 1

((- i) + (5 * j)) >= (k + 1)

1

x

- y <= j - k -1

(x - y) <= ((j - k) - 1)

1

x

+ k + 7 < y / k

((x + k) + 7) < (y / k)

0

|----------------------|---------------------------------|-------------|

------------------------------------------

Operatori si expresii de egalitate

-----------------------------------------

Expresiile pot contine si operatorii de egalitate == si !=. Expresiile ce le contin au valoarea 0 sau 1.

-----------

Exemple. Primele trei exemple sunt corecte, restul sunt gresite:

-----------

1. c == 'A'

3.

x + y == 2 * x - 5

4. a = b

5. a = = b - 1

6. (x + y) =! 44

Intuitiv, o expresie de egalitate cum ar fi a == b este sau "true" sau "false". Mai precis, daca a este

egal cu b, atunci a == b intoarce valoarea 1 (true); altfel, aceasta intoarce valoarea 0 (false). O expresie echivalenta este a - b == 0 (aceasta este ceea ce se implementeaza la nivel masina). Expresia "a != b" ilustreaza folosirea operatorului "diferit de" (sau "nu este egal cu").

-----------

Exemple: Vom considera urmatorul tabel cu declaratii si initializari.

-----------

Presupunem ca avem declaratiile:

int

i = 1, j = 2, k = 3;

-------------------------------------------------------------------------

|

-------------------------------------------------------------------------

Expresie

|

Expresie echivalenta

| Valoare

|

i == j

j == i

0

i != j

j != i

1

i + j + k == - 2 * - k

((i + j) + k) == ((-2) * (- k))

1

-------------------------|------------------------------------|----------|

------------------------------------------

Operatori logici si expresii logice

------------------------------------------

Operatorul logic ! este unar, iar && si || sunt binari. Expresiile ce contin acesti operatori intorc

valoarea 0 sau 1. Negarea logica poate fi aplicata unei expresii aritmetice sau unui tip pointer. Daca o expresie are valoarea 0, atunci expresia negata are valoarea 1. Daca expresia are o valoare diferita de 0, atunci expresia negata intoarce valoarea 1.

-----------

Exemple. Primele trei exemple sunt corecte, restul sunt gresite:

-----------

1. !a

2. !(x + 7.7)

3. !(a < b || c < d)

4. a!

5. a != b (este corecta, dar se refera la operatorul "diferit")

Unele identitati logice (din matematica) nu se "transmit" in C. De exemplu, se stie ca "not (not s)

=s", in timp ce valoarea lui "!!5" nu este 5, ci 1. Motivul este ca operatorul "!" se asociaza de la dreapta la stanga, si deci "!!5" este echivalent cu "!(!5)", care echivalent cu "!(0)", ce intoarce valoarea 1.

------------

Exemple: Vom considera urmatorul tabel cu declaratii si initializari.

------------

Presupunem ca avem declaratiile:

int

double x = 0.0, y = 999.9;

i = 7, j = 7;

--------------------------------------------------------------------

|

--------------------------------------------------------------------

Expresie

|

Expresie echivalenta

|

Valoare |

! (i - j) + 1

(! (i - j)) + 1

2

!

i

- j + 1

((! i) - j) + 1

-6

! ! (x + 3.3)

! (! (x + 3.3))

1

! x

*

!

! y

(! x) * (! (! y))

1

----------------------|------------------------------|-------------|

Operatorii logici binari && si || pot fi folositi in expresii care intorc 0 sau 1.

-----------

Exemple. Primele patru exemple sunt corecte, restul sunt gresite:

-----------

1. a && b

2. a || b

3. !(a < b) && c

4. 3 && (-2 * a + 7)

5. a &&

6. a | | b

7. (corecta, dar se refera la operatii peste biti)

a & b

8. (corecta, dar se refera la adresa lui b)-----------

Exemple: Vom considera urmatorul tabel cu declaratii si initializari.

-----------

Presupunem ca avem declaratiile:

&b

int

double x = 0.0, y = 2.3;

i = 3, j = 3, k = 3;

--------------------------------------------------------------------

|

--------------------------------------------------------------------

Expresie

|

Expresie echivalenta

|

Valoare |

i && j && k

x || i && j - 3

(i && j) && k

1

x || (i && (j - 3))

0

i j && x < y

<

(i < j) && (x < y)

0

i j || x < y

<

(i < j) || (x < y)

1

|----------------------|---------------------------------|------------|

--------------------------------------

Evaluare rapida (short-circuit)

--------------------------------------

Pentru expresiile ce contin && sau ||, evaluarea are loc cand s-a stabilit deja valoarea expresiei, eventual fara parcurgerea intregii expresii. Astfel, presupunem ca "expr1" se evalueaza la 0 (false). Atunci expresia expr1 && expr2

se va evalua la 0, fara a se mai face evaluarea expresiei "expr2". Alt exemplu, daca "expr1" se evalueaza la 1 (true), atunci expresia expr1 || expr2

se va evalua la true fara a se mai evalua expresia "expr2". Uneori se mai spune ca operatorii && si || sunt lazy (adica le este lene sa mai evalueze toti operanzii din expresie).

-----------------------------

Instructiunea compusa

-----------------------------

O instructiune compusa este un sir de declaratii si instructiuni delimitate de acolade. Ceea ce

acoladele delimiteaza se numeste "bloc". O instructiune compusa este ea insasi o instructiune.

-----------

Exemplu:

-----------

{

a = 1;

{

b = 2;

}

}

-------------------------

Instructiunea vida

-------------------------

Instructiunea vida se reprezinta cu semnul ; (punct si virgula). Ea se foloseste cand se doreste

folosirea ei sintactica, si nu neaparat folosire semantica. Dupa cum vom vedea, aceasta se foloseste in constructii "if-else" si "for". O expresie urmata de ; se numeste "instructiune expresie".

-----------

Exemplu:

-----------

a

a

;

printf("%d\n", a);

= b; + b + c;

--------------------------------------

Instructiunile "if" si "if-else"

--------------------------------------

Forma generala a instructiunii "if" este

if (expresie)

instructiune

Semantica intuitiva este simpla. Astfel, daca valoarea expresiei este true (diferita de zero), atunci se executa instructiunea, altfel nu.

-----------

Exemplu:

-----------

Instructiunea "if" de mai jos va testa daca se poate face impartirea cu y (ce trebuie sa fie diferit de

0):

if (y != 0.0) x /= y; Urmatoarele doua instructiuni if (j < k) min = j; if (j < k)

printf("j este mai mic decat k\n"); se pot scrie intr-una singura if (j < k)

{

min = j; printf("j este mai mic decat k\n");

}

Instructiunea "if-else" de mai jos este foarte apropiata de instructiunea "if". Aceasta are forma generala if (expresie)

instructiune1

else

instructiune2

Semantica intuitiva este de asemenea clara. Daca valoarea expresiei este diferita de zero, atunci se

executa instructiune1 si "se sare" peste instructiune2. Daca valoarea expresiei este zero, atunci "se sare" instructiune1, si se executa instructiune2.

------------

Exemplu:

------------

Urmatorul subprogram C de mai jos calculeaza si afiseaza minimul dintre x si y. if (x < y) min = x; else

min = y;

printf("Valoarea minima = %d\n", min);

-----------------------------

Instructiunea "while"

-----------------------------

"While", "for" si "do" sunt cele trei instructiuni repetitive din limbajul C. Consideram urmatorul format general al instructiunii "while" (iteratia sau bucla "while"). while (expresie) instructiune instructiune_urmatoare

Mai intai se evalueaza expresie. Daca aceasta nu este zero (deci este "true"), atunci se executa instructiunea, si control trece la inceputul buclei "while". Astfel, corpul buclei se executa de cate ori expresie se evalueaza la "true". Terminarea buclei are loc cand expresie ia valoarea zero (adica "false"). In acest punct, controlul se paseaza catre "instructiune_urmatoare".

-----------

Exemplu:-----------

while (i <= 10)

{

suma += i;

++i;

}

--------------------------

Instructiunea "for"

-------------------------

Ca si instructiunea "while", instructiunea "for" se foloseste pentru descrierea structurilor iterative (repetitive). Astfel constructia for (expresie1; expresie2; expresie3) instructiune instructiune_urmatoare este semantic echivalenta cu

expresie1;

while (expresie2)

{

instructiune;

expresie3;

}

instructiune_urmatoare; Deci, se va evalua expresie1. De obicei, aceasta se foloseste pentru initializarea buclei. Apoi, se

evalueaza expresie2. Daca aceasta nu este zero ("true"), atunci se executa instructiune, se evalueaza expresie3, si controlul buclei se "paseaza" la inceputul buclei (cu deosebirea ca nu se mai evalueaza expresie1). De obicei, expresie2 este o expresie logica care controleaza bucla. Acest proces continua pana cand expresie2 este 0 (false), punct in care se plaseaza controlul catre instructiune_urmatoare.

------------

Exemplu: Exemplul de mai jos calculeaza factorialul numarului n.

------------

factorial=1;

for (i = 1; i <= n; i++) factorial *= i;

Orice sau toate expresiile dintr-o instructiune "for" pot lipsi, dar nu poate lipsi ;.

-----------

Exemple:

-----------

Exemplul de mai jos calculeaza suma numerelor intregi de la 1 la 10.

i = 1;

suma = 0; for ( ; i <= 10; ++i)

suma += i;

Acesta se poate scrie echivalent:

i = 1;

suma = 0; for ( ; i <= 10; )

suma += i++;

Daca, in schimb, lipseste expresie2, atunci obtinem o bucla infinita.

---------------------

Operatorul ","

---------------------

Operatorul "," are cea mai mica prioritate dintre toti operatorii din C. Este un operator binar ce are

ca operanzi drept expresii si se asociaza de la stanga la dreapta. Intr-o expresie de forma expresie1 , expresie2

se evalueaza mai intai expresie1, apoi expresie2. Expresia "," intoarce valoarea si tipul operandului din dreapta.

-----------

Exemplu: Presupunem ca a, b sunt de tip int. Atunci expresia ","

------------

a = 0, b = 1 intoarce valoarea 1 de tipul int.

Operatorul "," este deseori folosit in instructiunea "for".

----------

Exemplu: Exemplul de mai jos calculeaza factorialul numarului n (reluare).

-----------

for (factorial = 1, i = 1; i <= n; i++)

factorial *= i;

------------

Exemplu: Revenim asupra unui exemplu precedent (suma primelor N numere naturale)

------------

for (suma = 0, i = 1; i <= n; ++i)

suma += i; se poate scrie, echivalent, in

for (suma = 0, i = 1; i <= n; suma += i, ++i);

-------------

Intrebare: Ce se intampla cu valoarea lui suma daca intervertim instructiunile

------------

 

suma += i

cu

++i

-----------

Exemplu:

-----------

------------------------

Instructiunea "do"

-----------------------

Instructiunea "do" poate fi considerata o varianta a instructiunii "while". Deosebirea consta in faptul

ca pentru instructiunea "while" testul se face la inceputul ciclului, iar pentru "do" la sfarsit. Consideram constructia de forma do

instructiune while (expresie);

instructiune_urmatoare La inceput se executa instructiune, apoi se evalueaza expresie. Daca valoarea lui expresie este diferita de 0 ("true"), atunci controlul se paseaza la inceputul instructiunii "do", si procesul se repeta. Daca expresie se evalueaza la 0 (false), atunci controlul se paseaza la instructiune_urmatoare.

-----------

Exemplu: Suma unor numere intregi diferite de 0

-----------

suma = i = 0;

do

{

suma += i;

scanf("%d", &i);

}

while (i > 0);

--------------------------

Instructiunea "goto"

-------------------------

Instructiunea "goto" (salt neconditionat) este considerata opusa programarii structurate. Sfatul

general valabil este evitarea acestei instructiuni. Totusi, in unele cazuri se poate folosi (cand simplifica controlul, cand face codul mai eficient). O instructiune de etichetare are forma:

eticheta : instructiune unde eticheta este un identificator.

------------

Exemple:

------------

bye: exit(1); eticheta1: a = b + c;

333: a = b + c; (exemplu gresit, de ce ?) Controlul programului poate fi transferat neconditionat catre o instructiune de etichetare astfel goto eticheta;

----------------------------------------------

Instructiunile "break" si "continue"

----------------------------------------------

Cele doua instructiuni

break;

si

continue;

intrerup controlul normal al programelor. Instructiunea "break" va cauza iesirea din bucla in care se afla sau din instructiunea "switch". Instructiunea "continue" se poate afla numai in instructiuni "for", "while" si "do". Ea are rolul de a trasmite controlul catre sfarsitul buclei respective.

-----------

Exemple:

-----------

while (1)

{

scanf("%lf", &x); if (x < 0.0)

break;

printf("%lf\n", sqrt(x));

/* iesim cand x este negativ */

}

while (contor < n)

{

scanf("%lf", &x);

if (x > -0.01 && x < =0.01)

continue; ++contor; suma += x;

/* valorile mici nu se iau in considerare */

}

------------------------------

Instructiunea "switch"

-----------------------------

"switch" este o instructiune conditionala ce generalizeaza o instructiune "if-else".

-----------

Exemplu:

-----------

switch (val)

{

case 1:

++contor_a;

break;

case 2:

case 3:

++contor_b;

break;

default:

++contor_c;

}

Corpul unei instructiuni "switch" este un exemplu de instructiune compusa. Expresia de control dintre paranteze (ce urmeaza cuvantului switch) trebuie sa fie de tip integral (vom reveni intr-un alt

capitol). Dupa evaluarea lui val, controlul sare la eticheta corespunzatoare valorii lui val. De obicei, ultima instructiune dintr-un "case" este de obicei "break". Daca nu exista "break", atunci se vor executa si instructiunile din urmatoarele "case"-uri. Atentie ! Omiterea scrierii lui "break" este foarte frecventa !! Poate apare cel mult un "default" (in general pe ultima pozitie). Cuvintele rezervate "case" si "default" pot apare numai in interiorul unui "switch".

------------------------------

Operatorul conditional

------------------------------

Operatorul "?:" este mai putin obisnuit deoarece este ternar (cu trei argumente). Forma generala este

expresie1 ? expresie2 : expresie3

Mai intai, se evalueaza expresie1. Daca aceasta este diferita de 0 (true), atunci se evalueaza

expresie2, si aceasta va fi valoarea returnata de intreaga expresie conditionala. Daca expresie1 este 0 (false), atunci se evalueaza expresie3, si aceasta va fi valoarea intregii expresii conditionale.

------------

Exemplu: Instructiunea

-----------

if (y < z)

x = y;

else

x = z;

este echivalenta cu

x = (y < z) ? y : z;

Operatorul ?: are aceeasi prioritate cu operatorul de asignare si se asociaza de la dreapta la stanga.

-----------------------------------------------

Exercitii propuse spre implementare

-----------------------------------------------

1.

Sa se scrie un program care sa calculeze minimul a trei numere (folosind o instructiune "if-then"

si

una "if" sau doua "if-then" (fara variabila suplimentara)). Generalizare: Sa se gaseasca primele

doua numere (cele mai mici) dintr-un vector de n elemente (cu numar minim de comparatii).

2. Cititi n numere de la tastatura si afisati maximul lor. Incercati sa cititi un numar arbitrar de

numere (deci fara a citi acest n).

3. Folosind structura for, scrieti un program care calculeaza urmatoarele formule logice (sub forma

unei tabele de adevar):

b1 || b3 || b5

si

b1 && b2 || b4 && b5

4. Fie functia lui Collatz:

{

n/2

daca n este par

f(n) =

{

3*n+1 daca n este impar

Sa se scrie un program C care determina k natural minim astfel incat

(f o f o

de k ori

o f)(n)=1.

5. Scrieti un program C care calculeaza suma divizorilor naturali ai unui numar natural n. Un numar

este perfect daca este egal cu suma divizorilor proprii pozitivi (ex: 28 = 1 + 2 + 4 + 7 + 14). Sa se genereze primele k numere perfecte (k < 5 !).

6. Operatia matematica min(x,y) se poate reprezenta ca o expresie conditionala:

(x < y) ? x : y

Intr-un mod similar, descrieti operatiile aritmetice

min(x, y, z)

si

max(x, y, z, t)

7.

Se stie ca un procedeu de interschimbare a valorii a doua variabile (a si b) se poate face folosind

o

variabila auxiliara (se foloseste in metodele de sortare, arbori, sisteme de ecuatii, etc):

aux =

a = b

a ;

; b = aux;

Sa se arate ca in limbajul C se poate face acest lucru in mod echivalent fara utilizarea explicita a unei variabile suplimentare. Asadar intervertirea valorilor a si b se poate face si astfel:

a = b + a - (b = a);

Aratati ca aceasta instructiune este echivalenta cu:

aux = b + a

b = a

a = aux - a;

;

;

(sau a = aux - b;)

Echivalent, fara variabile suplimentare, se pot considera instructiunile:

a = a + b;

b = a - b;

a = a - b;

Functii si programare structurata

4.1. Apelul functiilor

4.2. Instructiunea "return"

4.3. Prototipurile functiilor

4.4. Descriere "top-down"

4.5. Invocare si apel prin valoare

4.6. Deosebirea dintre "return" si "exit"

4.7. Exercitii propuse spre implementare

========

Capitolul 4

========

========================= Functii si programare structurata ========================= Programarea structurata este o problema ce rezolva strategia si metodologia programarii si are urmatoarele principii:

1. Structurile de control trebuie sa fie cat se poate de simple;

2. Constructia unui program trebuie sa fie descrisa top-down.

Descrierea top-down se refera la descompunerea problemei noastre in subprobleme. De obicei, aceste subprobleme sunt usor de descris.

---------------------

Apelul functiilor

---------------------

Un program este compus din una sau mai multe functii, printre care si "main()". Intotdeauna

executia unui program incepe cu "main()". Cand o functie este apelata (sau invocata) atunci controlul programului este pasat functiei apelate. Dupa ce aceasta isi termina executia, atunci se paseaza inapoi controlul catre program. Codul C care descrie ce face o functie se numeste "definitia functiei". Aceasta are urmatoarea forma generala:

tip nume_functie (lista_parametri)

{

declaratii

instructiuni

}

Primul rand se numeste "header-ul" (antetul) functiei, iar ceea ce este inclus intre acolade se numeste corpul functiei. Daca in antet nu precizam parametri, atunci se va scrie "void" (cuvant rezervat pentru lista vida). Daca functia nu

intoarce nici o valoare, atunci se va scrie ca tip intors tot "void". Tipul intors de functie este cel precizat in "return" (ce va fi indata explicat). Parametrii din antetul functiei sunt dati printr-o lista cu argumente separate prin virgula. Aceste argumente sunt date de tipul argumentului urmat de un identificator ce apartine acelui tip. Se mai spune ca acel identificator este "parametru formal".

-----------

Exemplu:

-----------

#include <stdio.h>

void tipareste_mesaj(int k)

{

int i; printf("Iti urez:\n"); for (i = 0; i < k; ++i) printf(" O zi buna ! \n");

}

main()

{

int n; printf("Dati un numar natural mic: "); scanf("%d", &n); tipareste_mesaj(n);

}

------------------------------

Instructiunea "return"

------------------------------

Instructiunea "return" este folosita pentru doua scopuri. Cand se executa o instructiune "return", controlul programului este pasat inapoi programului apelant. In plus, daca exista o expresie dupa acest "return", atunci se va returna valoarea acestei expresii. Instructiunea "return" poate avea formele:

return;

sau

return expresie;

-----------

Exemplu: Minimul a doi intregi.

-----------

#include <stdio.h>

int min(int x, int y)

{

if (x < y)

return x;

else

return y

}

main()

{

int j, k, m;

printf("Dati doi intregi: "); scanf("%d%d", &j, &k);

m = min(j, k);

printf("\n%d este minimul dintre %d si %d.\n", m, j, k);

}

------------------------------

Prototipurile functiilor

------------------------------

In C, apelul unei functii poate apare inaintea declararii ei. Functia poate fi definita mai tarziu in acelasi fisier, sau in alt fisier sau dintr-o biblioteca standard. In ANSI C, prototipul functiei

remediaza problema punand la dispozitie numarul si tipul argumentelor functiei. Prototipul specifica, de asemenea, si tipul returnat de functie. Sintaxa prototipului unei functii este:

tip nume_functie (lista_tipuri_parametri); In lista de parametri putem specifica chiar si parametrul, dar asta este optional. Daca functia nu are parametri, atunci se foloseste "void".

-----------

Exemplu: Reluam un exemplu precedent.

-----------

#include <stdio.h> main()

{

int n; void tipareste_mesaj(int);

printf("Dati un numar natural mic: "); scanf("%d", &n); tipareste_mesaj(n);

}

void tipareste_mesaj(k)

{

int i;

printf("Iti urez:\n"); for (i = 0; i < k; ++i) printf(" O zi buna ! \n");

}

Prototipul unei functii poate fi plasat in corpul altei functii, sau de regula, se scriu la inceputul programelor dupa directivele #include si #define.

----------------------------

Descriere "top-down"

----------------------------

Presupunem ca avem de citit cativa intregi si trebuie sa-i afisam in ordine pe coloane (in capatul de sus al coloanelor trebuie sa scriem numele campului), sa le afisam suma lor partiala, minimul si maximul lor. Pentru scrierea unui program C ce face acest lucru, vom utiliza proiectarea (descrierea) "top-down". Astfel, descompunem problema in urmatoarele subprobleme:

1. Un antet pentru problema data;

2. Scrierea campurilor;

3. Citirea si scrierea lor pe coloane.

Toti acesti trei pasi vor fi descrisi in cate o functie ce se apeleaza din "main()". Obtinem, un prim cod:

#include <stdio.h>

main()

{

void tipareste_antet(void); void scrie_campurile(void); void citeste_scrie_coloanele(void);

tipareste_antet();

scrie_campurile();

citeste_scrie_coloanele();

}

Aceasta reprezinta intr-un mod foarte simplu descrierea "top-down". Daca o problema este prea grea, atunci o descompunem in subprobleme, si apoi le rezolvam pe acestea. Beneficiul suplimentar al acestei metode este claritatea sa.

void tipareste_antet(void)

{

printf("\n%s%s%s\n",

"**************************************************\n",

"*

"**************************************************\n");

Calculul sumelor, minimului si maximului

*\n",

}

Functia ce foloseste la scrierea campurilor este la fel usor de scris:

void scrie_campurile(void)

{

printf("%5s%12s%12s%12s%12s\n\n",

"Numar", "Articol", "Suma", "Minimul", "Maximul");

}

Urmeaza apoi functia ce serveste la scrierea inregistrarilor referitoare la campurile discutate mai sus:

void citeste_scrie_coloanele(void)

{

int contor = 0, articol, suma, minim, maxim;

int min(int, int), max(int, int);

if (scanf("%d", &articol) == 1)

{

++contor; suma = minim = maxim = articol;

printf("%5d%12d%12d%12d%12d\n\n",

contor, articol, suma, minim, maxim); while (scanf("%d", &articol) == 1)

{

++contor; suma += articol; minim = min(articol, minim); maxim = max(articol, maxim);

printf("%5d%12d%12d%12d%12d\n\n",

contor, articol, suma, minim, maxim);

}

}

else printf("Nici o data nu a fost citita.\n\n");

}

Daca datele se introduc de la tastatura, atunci tabelul se va afisa "intrerupt" de citirile ce au loc de la tastatura. Astfel, se prefera citirea dintr-un fisier extern. Presupunem ca fisierul nostru executabil (asociat fisierului sursa scris in C) se numeste "numere.exe" si am creat un fisier numit "fisier.int" ce contine urmatoarele numere:

19

23

-7

29 -11

17

Dand comanda numere < fisier.int vom obtine un tabel ce contine toate datele dorite.

-------------------------------------

Invocare si apel prin valoare

-------------------------------------

O functie este invocata prin scrierea numelui sau impreuna cu lista sa de argumente intre paranteze. De obicei, numarul si tipul acestor argumente se "potriveste" cu parametrii din lista de parametri

prezenti in definitia functiei. Toate argumentele sunt apelate prin valoare ("call-by-value"). Asta inseamna ca fiecare argument este evaluat si valoarea sa este folosita ca valoare pentru parametrul formal corespunzator. De aceea, daca o variabila (argument) este folosita la transmiterea unei valori, atunci valoarea ei nu se schimba.

-----------

Exemplu:

-----------

#include <stdio.h>

main()

{

int n=3, suma, calculeaza_suma(int);

printf("%d\n", n);

suma = calculeaza_suma(n);

printf("%d\n", n);

printf("%d\n", suma);

/* se va scrie 3 */

/* se va scrie 3 */ /* se va scrie 6 */

}

int calculeaza_suma(int n)

{

/* suma numerelor de la 1 la n */

int suma = 0;

for ( ; n > 0; --n) sum += n; printf("%d\n", n); return suma;

/* n se schimba aici, dar nu si in main() */

/* se va scrie 0 */

}

Chiar daca n este trimis ca argument in functia "calculeaza_suma()" si valoarea lui n se modifica in aceasta functie, valoarea sa din mediul apelant ramane neschimbata. Vom vedea mai tarziu cum se poate simula apelul prin adresa ("call-by-reference").

----------------------------------------------

Deosebirea dintre "return" si "exit"

----------------------------------------------

Exista doua procedee de a returna o valoare.

return expresie

si

exit(expresie)

Daca se folosesc in "main()", atunci acestea sunt echivalente, dar in orice alta functie efectul lor este diferit (in ANSI C, functia "main()" intoarce o valoare de tip int). Un apel al lui exit() in orice

alta functie va implica terminarea executiei programului si returnarea valorii catre mediul apelant (sistemul de operare sau mediul de programare C). Valoarea returnata se numeste stare de iesire ("exit status"). Prin conventie, starea de iesire zero indica terminare cu succes, pe cand iesire cu un numar diferit de zero indica o situatie anormala.

-----------------------------------------------

Exercitii propuse spre implementare

-----------------------------------------------

1. Folosind functiile "rand()", "min(,)" si "max(,)", sa se genereze n numere naturale si sa se

afiseze minimul si maximul dintre acestea.

2. (Jocul cap-pajura, simulare Monte-Carlo)

Presupunem ca dispunem de o moneda ideala (nemasluita). Doi jucatori arunca cu moneda dupa urmatoarele reguli:

1.a. Se fac un numar total de n aruncari; 1.b. Primul jucator arunca moneda si celalalt spune "cap" sau

1.c. Daca acesta "ghiceste" ce va pica moneda, atunci se inverseaza jucatorii (adica arunca al

"pajura";

doilea si primul incearca sa

ghiceasca);

1.d. La sfarsit, trebuie afisat scorul (si procentul de castig al fiecaruia).

3. (Conjectura lui Goldbach)

Orice numar par mai mare decat 2 se poate scrie ca suma a doua numere prime. Scrieti un

program C care verifica aceasta conjectura pentru numere situate intre m si n. De exemplu, daca m=700 si n=1100, atunci afisati:

700

= 17 + 683

702

= 11 + 691

704

=

3 + 701

1098

=

5 + 1093 3 + 1097

= Generalizare: Scrieti toate combinatiile posibile de adunare a doua numere prime egal cu un numar dat.

1100

Procesarea caracterelor

5.1. Tipul de data "char"

5.2. Utilizarea lui "getchar()" si "putchar()"

5.3. Biblioteca <ctype.h>

5.4. Un exemplu util: Numararea cuvintelor

5.5. Exercitii propuse spre implementare

======== Capitolul 5 ======== ================== Procesarea caracterelor ==================

---------------------------

Tipul de data "char"

---------------------------

Este unul dintre tipurile fundamentale din limbajul C. Constantele si variabilele de acest tip sunt folosite pentru reprezentarea caracterelor. Fiecare caracter este memorat pe 1 byte (octet), care (in

general) este compus din 8 biti. Un octet compus din 8 biti poate pastra 2^8=256 valori distincte. Cand memoram un caracter intr-un octet, continutul acestuia poate fi gandit ca un caracter sau un

intreg mic (intre 0 si 255). Desi putem memora 256 valori distincte, doar o parte din ele sunt tiparibile (litere mici, mari, cifre, semne de punctuatie, spatiu, tab, caractere speciale +, *, %). Exemple de caractere netiparibile: newline, bell.

O constanta caracter se scrie intre apostroafe, cum ar fi: 'a', 'b'. O declaratie obisnuita a unei

variabile de tip caracter este:

char c; Variabilele caracter se pot initializa astfel:

char c1 = 'A', c2 = '*';

Un caracter este pastrat in memorie pe un octet dupa o codificare specifica. Multe masini folosesc codurile de caractere ASCII sau EBCDIC. Ne vom referi numai la codul ASCII. Astfel, vom preciza constanta caracter si valoarea corespunzatoare a sa:

de la 2^5+2^4 pana la 57, in ordine: '0', '1', de la 2^6+2^0 pana la 90, in ordine: 'A', 'B',

de la 2^6+2^5+2^0 pana la 112, in ordine: 'a', 'b',

, '9'

, 'Z'

, 'z'

De exemplu, se observa ca pentru a obtine litere mici din cele mari, schimbam doar un bit. Astfel, caracterul 'A' are codul 65 care inseamna numarul 01000001 in baza 2, iar caracterul 'a' are codul 01100001. Se observa ca difera doar bitul cu numarul 3.

-----------

Exemple:

-----------

In functiile "printf()" si "scanf()", pentru formatul caracter se foloseste %c.

printf("%c", 'a');

printf("%c%c%c", 'A', 'B', 'C'); va tipari ABC

printf("%d", 'a'); printf("%c", 97);

va tipari a

va tipari 97 va tipari a

Anumite caractere netiparibile necesita "secvente escape" (\ reprezinta caracterul escape). In acest

sens, dam un tabel Numele caracterului

Modul de scriere

Valoarea intreaga

alert

\a

7

backslash

\\

92

backspace

\b

8

carriage return

\r

13

ghilimea

\"

34

formfeed

\f

12

tab orizontal

\t

9

newline

\n

10

caracterul nul

\0

0

apostrof

\'

39

tab vertical

\v

11

-----------

Exemple: Ce va fi afisat in cazul urmatoarelor instructiuni ?

-----------

1. printf("\"ABC\"");

2. printf("'ABC'");

Un alt mod de a scrie o constanta caracter este folosind una, doua sau trei cifre octale ca secvente escape, cum ar fi '\007'. Acest

'\007' este de fapt caracterul "alert" (sau clopotel). El mai poate fi scris '\07' sau '\7' sau \a.

----------------------------------------------------

Utilizarea lui "getchar()" si "putchar()"

----------------------------------------------------

Aceste functii sunt folosite pentru citirea si scrierea caracterelor si sunt definite in <stdio.h>. Astfel pentru citirea unui caracter de la tastatura se foloseste "getchar()", iar pentru scrierea unui caracter pe ecran "putchar()". Bineinteles ca daca dorim sa afisam un sir de caractere mai mare, este mai elegant cu functia "printf()".

-----------

Exemplu:

-----------

Urmatorul program citeste un caracter din intrare (tastatura) si il atribuie unei varibile de tip char,

apoi il afiseaza pe ecran. #include <stdio.h> main()

{

char c;

while (1)

{

c=getchar();

putchar(c);

}

}

Singurul mod de a opri acest program este sa apasam CTRL^C. Putem reface acest program folosind constanta EOF. #include <stdio.h> main()

{

int c;

while ((c = getchar()) != EOF)

{

putchar(c);

}

}

--------------

Comentarii:

--------------

1. In biblioteca <stdio.h>, exista o linie in care se declara #define EOF (-1)

Denumirea lui EOF provine de la "end-of-file".

2. Variabila c trebuie sa fie declarata de tip int si nu de tip char. Am vazut ca sfarsitul unui fisier

este codificat cu -1, si nu cu un caracter.

3. Subexpresia

c=getchar() citeste o valoare de la tastatura si o asigneaza variabilei c.

--------------------------

Biblioteca <ctype.h>

--------------------------

Sistemul C pune la dispozitie fisierul header <ctype.h> care contine o multime de macro-uri (definitii) folosite pentru testarea

caracterelor si o multime de prototipuri de functii ce sunt folosite pentru conversia caracterelor. In tabelul de mai jos prezentam o lista de macro-uri folosite la testarea caracterelor. Aceste macro- uri iau ca argument o variabila de tip int si returneaza o valoare de tip int (zero=false, diferit de zero=true).

-----------------------------------------------------------------------

|

-----------------------------------------------------------------------

Macro

|

Se returneaza true (diferit de zero) daca

|

isalpha(c)

c este litera

isupper(c)

c este litera majuscula

islower(c)

c este litera mica

isdigit(c)

c este cifra

isalnum(c)

c este litera sau cifra

isxdigit(c)

c este cifra hexazecimala

isspace(c)

c este caracter spatiu

ispunct(c)

c este semn de punctuatie

isprint(c)

c este caracter tiparibil

isgraph(c)

c este tiparibil, dar diferit de spatiu

iscntrl(c)

c este caracter de control

isascii(c)

c este cod ASCII

-------------------------------------------------------------------

In tabelul urmator, vom scrie functiile "toupper()" si "tolower()", care sunt din biblioteca standard si macro-ul "toascii()". Macro-ul

si prototipurile pentru cele doua functii sunt in <ctype.h>. Acestea au ca argument o variabila de tip int si returneaza tipul int.

toupper(c)

schimba c din litera mica in majuscula

tolower(c)

schimba c din majuscula in litera mica

toascii(c)

schimba c cu codul ASCII

----------------

Un exemplu (util): Numararea cuvintelor

----------------

Vrem sa numaram cate cuvinte sunt introduse de la tastatura. Ele sunt separate prin spatiu. Pentru scrierea programului vom utiliza tot strategia "top-down".

#include <stdio.h> #include <ctype.h> main()

{

int numar_cuvinte = 0; int gaseste_urmatorul_cuvant(void);

while (gaseste_urmatorul_cuvant() == 1) ++ numar_cuvinte;

printf("Numarul de cuvinte = %d\n\n", numar_cuvinte);

}

int gaseste_urmatorul_cuvant(void)

{

int c;

while (isspace(c = getchar()))

;

if (c != EOF)

{

/* sarim peste spatii */

while ((c = getchar()) != EOF && !isspace(c))

; /* sarim peste orice diferit de EOF si spatii */ if (c != EOF) return 1; else return 0;

}

return 0;

}

-----------------------------------------------

Exercitii propuse spre implementare

-----------------------------------------------

1. Folosind functiile "getchar()" si "putchar()", sa se scrie un program C care transforma literele

mici in litere mari. Incercati si o varianta de program care foloseste functiile "islower()" si

"toupper()".

2. Utilizand functiile "getchar()" si "putchar()" creati un program C care sa copie un fisier in alt

fisier (comanda voastra proprie de

copiere). Utilizati redirectarea ! De asemeni, precizati si cazul cand dorim sa copiem un fisier la sfarsitul unui fisier existent.

3. Scrieti in C un analizor lexical care sa recunoasca cat mai multi atomi lexicali din C. De

identificatori, operatori (relationali, logici,

exemplu, cuvintele rezervate (while, do, for,

artimetici,

multiple si a comentariilor.

),

) si eventual alte structuri. Apoi, tipariti acelasi fisier de intrare cu exceptia spatiilor

4. Scrieti un program C care citeste caractere de la tastatura si le scrie la ecran. Scrieti toate

vocalele cu litere majuscule si consoanele cu litere mici. Apoi, scrieti un program C care citeste

caractere de la tastatura si sterge vocalele din ele (afisand doar consoanele). (Acest mod de scriere era folosit in scrisul hieroglific al Greciei Antice). 5. ("Pretty printing") Scrieti un program C care are la intrare un fisier sursa C si il transforma intr-un program C scris frumos (eventual in stilul Bell Laboratoires).

Tipuri fundamentale de date

6.1. Declaratii si expresii

6.2. Tipuri fundamentale de date

6.3. Caractere si tipul "char"

6.4. Tipul de date "int"

6.5. Tipurile integrale "short", "long" si "unsigned"

6.6. Tipuri reale

6.7. Operatorul "sizeof()"

6.8. Functii matematice

6.9. Conversii implicite si explicite

6.10. Conversia la intreg

6.11. Conversiile aritmetice uzuale

6.12. Conversii explicite

6.13. Erori de programare frecvente

6.14. Exercitii propuse spre implementare

======== Capitolul 6 ======== ===================== Tipuri fundamentale de date =====================

---------------------------

Declaratii si expresii

---------------------------

Variabilele si constantele sunt obiecte cu care se lucreaza intr-un program. In C, toate variabilele trebuie declarate inainte de a fi

folosite. Declaratiile au doua scopuri:

1. spun compilatorului cat spatiu de memorie trebuie rezervat pentru memorarea acelor variabile;

2. permit compilatorului sa instruiasca masina pentru a face operatiile specifice corect.

De exemplu, in expresia a + b, operatorul + este aplicat pentru doua variabile. Masina executa in mod diferit adunarea pentru variabile de tip "int" si pentru variabile de tip "float". Bineinteles, pentru programator aceste conventii sunt transparente (se mai spune ca "+" este operator de supraincarcare). Expresiile sunt combinatii (cu inteles) de constante, variabile si apeluri de functii. Majoritatea expresiilor (cum ar fi, de exemplu, variabilele) au si valoare si tip. In multe situatii, valoarea returnata depinde in principal de tipul expresiei.

--------------------------------------

Tipuri fundamentale de date

--------------------------------------

Avem urmatoarele tipuri fundamentale de date (scriere intreaga - lunga):

char

signed char

unsigned char

signed short int

signed int

signed long int

unsigned short int

unsigned int

unsigned long int

float

double

long double

Toate acestea sunt cuvinte rezervate, deci nu se pot folosi ca nume de variabile. Alte tipuri de date, cum ar fi vectorii si pointerii, sunt derivate din tipurile fundamentale. De obicei, cuvantul rezervat "signed" nu se mai scrie. De exemplu, "signed int" este echivalent cu "int". De asemenea, cuvintele "short int", "long int" si "unsigned int" pot fi prescurtate, de obicei, ca

"short", "long" si "unsigned". Cu aceste conventii, tabelul de mai sus se mai poate scrie:

char

signed char

unsigned char

short

int

long

unsigned short

unsigned

unsigned long

float

double

long double

Tipurile fundamentale se pot grupa dupa functionalitate:

1. tipurile integrale sunt cele care sunt folosite pentru reprezentarea valorilor intregi;

2. tipurile reale sunt cele care sunt folosite pentru reprezentarea valorilor reale;

3. tipurile aritmetice sunt tipuri integrale sau reale.

Acestea sunt:

Tipuri integrale:

char

signed char

unsigned char

short

int

long

unsigned short Tipuri reale:

unsigned

unsigned long

float

double

long double

---------------------------------

Caractere si tipul "char"

---------------------------------

In C, variabilele de orice tip integral pot fi folosite pentru reprezentarea caracterelor. In particular, variabilele de tip "char" si "int" se folosesc pentru acest scop. Am vazut in capitolul precedent ca atunci cand dorim sa comparam o variabila cu EOF, atunci trebuie sa declaram acea variabila de tip "int", si nu de tip "char". Constante cum ar fi 'a', '+' pe care le gandim ca fiind caractere sunt de tip "int", si nu de tip "char". Retineti ca nu exista constante de tip "char" !!! Reamintim ca toate caracterele sunt tratate ca "intregi mici", si reciproc, intregii mici sunt tratati ca niste caractere. In particular, orice expresie integrala poate fi afisata in format intreg sau caracter.

-----------

Exemplu: Presupunem ca avem o "bucata" de cod C:

-----------

char c = 'a';

int

i = 65;

printf("%c", c + 1); printf("%d", c + 2); printf("%c", i + 3);

/* 'a' are codul ASCII 97 */ /* 65 este codul ASCII pentru 'A' */

/* este afisat b */ /* este afisat 99 */ /* este afisat D */

In C, fiecare caracter este memorat pe un octet de memorie. Pe aproape toate masinile, un octet este

compus din 8 biti. Fie declaratia

char c = 'a'; Putem gandi ca "c" este memorat pe un octet astfel

------------------------------

|

------------------------------

0 |

0

|

1

|

1

|

0

|

0

|

0

|

1

|

7

6

5

4

3

2

1

0

Fiecare celula reprezinta un bit si fiecare bit este numerotat (incepand cu cel mai putin

semnificativ). Bitii care formeaza un octet sunt fie "on", fie "off", aceste stari fiind reprezentate prin 1 si 0 respectiv. Acesta ne conduce sa gandim fiecare octet din memorie ca un sir de 8 cifre binare (se mai numesc siruri de biti). Astfel variabila "c" poate fi gandita ca sirul de biti

01100001

Mai general, fiecare cuvant masina poate fi gandit ca un sir de cifre binare grupate in octeti.

Un sir de cifre binare poate fi deci gandit ca un numar binar (adica in baza 2). Fara a intra in detalii matematice (teorema bazei de numeratie) vom face doar un exemplu:

-----------

Exemplu:

-----------

Valoarea lui "c" este numarul 01100001 (in baza 2) 1 x 2^6 + 1 x 2^5 + 0 x 2^4 + 0 x 2^3 + 0 x 2^2 + 0 x 2^1 + 1 x 2^0 care inseamna 64 + 32 + 1 = 97 in notatia zecimala (in baza 10). ANSI C pune la dispozitie trei tipuri referitoare la caractere:

char

signed char

unsigned char

De obicei, tipul "char" este echivalent cu "signed char" sau "unsigned char", depinzand de compilator. Fiecare din aceste trei tipuri se memoreaza pe un octet (deci poate "tine" 256 valori

distincte). Pentru "signed char", valorile sunt intre -128 si 127, iar pentru "unsigned char" intre 0 si

255.

-------------------------

Tipul de date "int"

-------------------------

Tipul de date "int" este cel mai folosit tip din limbajul C. Acest tip, impreuna cu alte tipuri integrale (cum ar fi: "char", "short" si "long") este desemnat pentru lucrul cu valori intregi reprezentabile pe o masina.

In matematica, numerele naturale sunt 0, 1, 2, 3,

, care impreuna cu cele negative

(corespunzatoare) formeaza numerele intregi. Pe o masina, se pot reprezenta (folosind un tip integral) numai o submultime finita a acestor numere. De obicei, un cuvant se memoreaza pe un cuvant masina. Anumite calculatoare folosesc cuvante de 2 octeti (=16 biti), altele 4 octeti (=32 biti).

-----------

Exemple:

-----------

1. Masini ce folosesc cuvinte memorate pe 2 octeti: PC

2. Masini ce folosesc cuvinte memorate pe 4 octeti: Apollo, Hewlett-Packard, Next, Silicon

Graphics, Sun, etc.

Presupunem ca lucram pe un calculator care lucreaza pe 4 octeti. Aceasta implica ca un cuvant are

32 biti, deci poate "tine" 2^{32} valori distincte. Jumatate sunt folosite pentru reprezentarea numerelor negative si cealalta jumatate pentru pozitive:

- 2^{31}, -2^{31}+1,

, -2, -1, 0, 1, 2,

, 2^{31}-1

Daca lucram pe un calculator unde memorarea unui cuvant se face pe 2 octeti, atunci putem memora 2^{16} valori distincte. Valoarea cea mai mare, a tipului "int" este data de constanta MAXINT. Evident cea mai mica valoare va fi -MAXINT-1. Daca se incearca, de exemplu, adunarea a doua numere (si se depaseste aceasta valoare), atunci se va primi un mesaj "integer overflow".

-------------------------------------------------------------

Tipurile integrale "short", "long" si "unsigned"

-------------------------------------------------------------

De obicei, tipul "short" se memoreaza pe doi octeti si tipul "long" pe patru octeti. Astfel, pe masinile in care cuvintele au patru octeti, lungimea tipului "int" este aceeasi cu lungimea tipului

"long", iar pe masinile in care cuvintele au doi octeti, lungimea tipului "int" este egala cu lungimea tipului "short". Constantele predefinite MAXSHORT si MAXLONG (in unele implementari LONG_MAX) caracterizeaza lungimea acestor tipuri. De obicei, MAXSHORT=2^{15} si MAXLONG=2^{31}. Astfel, daca "s" este o variabila de tip "short", atunci - MAXSHORT <= s <= MAXSHORT-1 Daca "l" este o variabila de tip "long", atunci - MAXLONG <= s <= MAXLONG-1 In ceea ce priveste tipul "unsigned", acesta este memorat pe acelasi numar de octeti ca si tipul "int". Daca "u" este o variabila de tip "unsigned", atunci 0 <= u <= 2*MAXINT-1

----------------

Tipuri reale

----------------

ANSI C contine trei tipuri reale: "float", "double" si "long double". Variabilele de acest tip vor putea tine valori reale, cum ar fi:

0.001 2.0 3.14159 Aceasta notatie se numeste notatie zecimala, deoarece contine punctul zecimal. Mai exista si notatia exponentiala. De exemplu, 1.234567e5 corespunde cu 1.234567 x 10^5=123456.7

Pe majoritatea masinilor, tipul "float" se memoreaza pe 4 octeti, iar tipul "double" pe 8 octeti. Asta inseamna ca o variabila de tipul "float" poate avea 6 zecimale, iar o variabila de tipul "double" poate avea 15 zecimale. Astfel, o variabila de tipul "float" are forma 0,d_1 d_2 d_3 d_4 d_5 d_6 x 10^{n} unde -38 <= n <= 38. Asemanator, o variabila de tipul "double" are forma

0,d_1 d_2 unde -308 <= n <= 308. Astfel, instructiunea

x = 123.45123451234512345; /* 20 cifre semnificative */ va implica atribuirea lui x a valorii

d_{15} x 10^{n}

0.123451234512345 x 10^3

(15 cifre semnificative)

In ANSI C, pentru varibilele de tip "long double" se aloca mai multa memorie. Insa sunt compilatoare care trateaza acest exact tip exact ca si "double".

----------------------------

Operatorul "sizeof()"

----------------------------

C pune la dispozitie operatorul "sizeof()" pentru determinarea numarului de octeti necesari memorarii unui obiect. Acesta are aceeasi prioritate si asociativitate ca si ceilalti operatori unari. O expresie de forma sizeof(obiect) returneaza un intreg car reprezinta numarul de octeti necesari pentru memorarea obiectului in memorie. Un obiect poate fi un tip, cum ar fi "int" sau "float", sau poate fi o expresie, cum ar fi a + b, sau poate fi un sir sau o structura.

-----------

Exemplu: Calculul numarului de octeti pentru cateva tipuri

-----------

#include <stdio.h>

main()

{

printf("Lungimea catorva tipuri fundamentale.\n\n");

printf("

char:%3d octeti \n", sizeof(char));

printf("

short:%3d octeti \n", sizeof(short));

printf("

int:%3d octeti \n", sizeof(int));

printf("

long:%3d octeti \n", sizeof(long));

printf("

unsigned:%3d octeti \n", sizeof(unsigned));

printf("

float:%3d octeti \n", sizeof(float));

printf("

double:%3d octeti \n", sizeof(double));

printf("long double:%3d octeti \n", sizeof(long double));

}

Din moment ce limbajul C este flexibil in ceea ce priveste necesarul de memorie pentru tipurile fundamentale, situatiile pot sa difere de la o masina la alta. Totusi, aceasta garanteaza ca:

sizeof(char) = 1 sizeof(short) <= sizeof(int) <= sizeof(long) sizeof(signed) <= sizeof(unsigned) <= sizeof(int) sizeof(float) <= sizeof(double) <= sizeof(long double) "sizeof()" nu este o functie (chiar daca contine paranteze atunci cand ne referim la tipuri), ci este un operator. De exemplu:

sizeof(a + b + 7.7) este echivalent cu sizeof a + b + 7.7

-------------------------

Functii matematice

-------------------------

Nu exista functii matematice implicite (in compilatorul C), ci acestea sunt descrise in biblioteci. De

exemplu, functiile sqrt() pow() exp() log() sin() cos() tan() sunt definite in biblioteca <math.h>. Toate aceste functii, cu exceptia lui "power()" au un argument de tip "double" si returneaza o valoare de tip "double". Functia "power()" are doua argumente de tip "double" si returneaza o valoare de tip "double".

---------------------------------------

Conversii implicite si explicite

---------------------------------------

O expresie aritmetica, cum ar fi "x + y", are si valoare si tip. De exemplu, daca "x" si "y" au tipul

"int", atunci expresia "x + y" are

tipul "int". Dar, daca "x" si "y" au ambele tipul "short", atunci "x + y" este de tip "int", si nu "short". Aceasta se intampla deoarece