Fundamentele procedurală

programării

şi

programare

Curs 1 Introducere în limbaje de programare
Obiective:
Acest curs are scopul de a forma o privire de ansamblu asupra programării în general şi a programării C în particular.

C este numit un limbaj de programare „de nivel mediu”. Această etichetă nu este datorată unei lipse de „putere” a limbajului de programare (din potrivă C este considerat de mul i ca un limbaj „prea puternic”) ci mai degrabă a posibilitatea de a accesa func ii de sistem de nivel coborât. Majoritate limbajelor de nivel înalt Pasal, Java etc. presupune faptul că orice ar avea nevoie programul există deja în limbaj. Un limbaj de nivel coborât (de exemplu un limbaj de asamblare) furnizează doar un acces la instruc iunile de bază ale unită ii centrale. Un limbaj de nivel intermediar ca de exemplu C nu are toate construc iile găsite în limbajele de nivel înalt dar furnizează un mod de programare suficient de elegant asemănător unui limbaj de nivel înalt oferind pe de altă parte şi unelte opera ii şi facilită i întâlnite la limbajele de nivel coborât.

Folosirea limbajului C
C a fost folosit la început pentru a dezvolta sisteme de operare. Practic peste 80% din Linux este scris în C. De ce? Pentru că programul C generat rulează aproape la fel de rapid ca cel scris în limbaj de asamblare, dar modul de scriere al programelor este asemănător cu cel al limbajelor de nivel înalt. Ce fel de programe sunt scrise în C: • Sisteme de operare • Compilatoare de limbaj (compilatoarele de C dar nu numai sunt de obicei scrise in C) • Asambloare • Editoare de text • Drivere de re ea • Baze de date sau programe care gestionează baze de date • Interpretoare de comenzi • Programe utilitare • etc. Popularitate limbajului C a făcut ca alte limbaje de programare moderne sa fie „inspirate” din C ca func ionalitate sau sintaxă; dintre acestea aş aminti Java şi php dar sunt multe altele.

Limbaje de programare
Limbajul de programare poate fi definit astfel: O unealtă software ce permite unui programator să scrie comenzi într-un format care poate fi uşor de în eles şi re inut de o persoană şi poate fi tradus într-un cod pe care un calculator îl poate în elege şi executa. Există foarte multe limbaje de programare El este apropiat de limbajul natural şi de aceea este un excelent mod de a explica un algoritm. Din exemplul de mai sus reiese cu claritate finitudinea algoritmului şi eficacitatea acestuia. Este un algoritm generalizat deoarece este conceput pentru a rezolva orice ecua ie de gradul I. Pentru a putea fi generalizată, orice solu ie algoritmică are nevoie de a verifica datele din toata plaja posibilă de valori. Algoritmului trebuie să poată accepta date de intrare, de a le prelucra şi de a le afişa ca rezultate. Aceste lucruri nu sunt posibile decât dacă datele pot fi "memorate". Cea mai folosită cale de a memora şi identifica date este aceea de a folosi variabile.

De ce să folosim limbajul C?
Limbajul de programare C în ciuda „vârstei” sale destul de înaintate este şi rămâne un limbaj de succes în rezolvarea oricărei probleme de programare imaginate de cineva, de la programe parte integrantă a sistemelor de operare până la programe de inteligen ă artificială, oferind compilări eficiente pe toate calculatoarele existente de la controlerele robo ilor industriali până la supercalculatoare. Acest succes se datorează folosirii unitare si bine structurate a următoarelor concepte practice: • O varietate mare si puternică de operatori; • O sintaxă elegantă; • Conceptul de biblioteci standard; • O accesibilitate sporită la nivel hardware; • Posibilitatea facilă de a optimiza codul pentru fiecare bucă ică de program în parte • Portabilitate între calculatoare;

Istoric
C este un limbaj de uz general, care este asociat de sistemul de operare UNIX (din care a derivat apoi şi Linux), sistem care foloseşte intensiv acest limbaj fiind în foarte mare măsură scris în C. Ideile din limbajul C au fost preluate din limbajul BCPL, dezvoltat de Martin Richards. Fiind precedat de limbajul B scris de Ken Thompson în 1970 la Bell Labs, pentru primul sistem UNIX pe calculatore DEC PDP-7. În 1972 Dennis Ritchie de la Bell Labs scrie limbajul C şi în 1978 publică „The C Programming Language” de Kernighan & Ritchie având ca rezultat ă revolu ie în lumea calculatoarelor. În 1983, American National Standards Institute (ANSI Institutul Na ional de Standarde American) stabileşte o definire clară a termenelor pentru limbajul C, rezultatul fiind definirea standardului ANSI pentru C supranumit "ANSI C", care apoi a fost completat în 1988. 1

Curs 2 No iunea de algoritm
Def.1. Un algoritm este o succesiune de paşi finită, cu punct unic de intrare şi de ieşire, care conduce într-un timp finit la rezolvarea unei clase de probleme. Def2. Algoritmul este un mod prin care o transformare este descrisa sistematic şi condi ional în desfăşurarea sa. Transformarea impune de obicei folosirea unor valori numite date de intrare sau parametrii de intrare pe care algoritmul le transformă în rezultate sau date de ieşire. Algoritmii au următoarele caracteristici:

Date de intrare

Algoritm

Date de ieşire

• generalizare: adică sunt concepu i pentru a rezolva cu succes mai multe probleme care au o cale de rezolvare asemănătoare • determinism: adică ob inerea de rezultate identice pentru valori de intrare identice • finitudine: procesul de prelucrare se termină după un număr finit de paşi; (există cazuri în care programele scrise nu respectă această condi ie ceea ce conduce la buclă infinită) • claritate: toate ac iunile sau condi ionările din algoritm sunt clar specificate Exemplu1: Rezolvarea unei ecua ii de gradul I. Analiza problemei: Forma generala a unei ecua ii de gradul I este ax+b=0. Date de intrare: a şi b. Date de ieşire: rădăcina calculata x a ecua iei sau un mesaj ("nu exista solu ie" sau "Există o infinitate de solu ii") Procesul prelucrării datelor de intrare poate fi descris astfel: PAS 1. Se citesc valorile coeficien ilor a, b; PAS 2. Dacă a=0 şi b=0 atunci se afişează "Există o infinitate de solu ii"; PAS 3. Dacă a=0 şi b<>0 atunci se afişează "nu exista solu ie"; PAS 4. Dacă a<>0 atunci solu ia este x=-b/a; PAS 5. Afişează valoarea lui x; Pentru a reprezenta un algoritm putem alege orice model care repezită dezideratele arătate mai sus. Algoritmul din exemplul anterior a fost reprezentat prin pseudocod. Pseudocodul este un mod de a descrie liber (fără îngrădiri) a unui algoritm.

El este apropiat de limbajul natural şi de aceea este un excelent mod de a explica un algoritm. Din exemplul de mai sus reiese cu claritate finitudinea algoritmului şi eficacitatea acestuia. Este un algoritm generalizat deoarece este conceput pentru a rezolva orice ecua ie de gradul I. Pentru a putea fi generalizată, orice solu ie algoritmică are nevoie de a verifica datele din toata plaja posibilă de valori. Algoritmului trebuie să poată accepta date de intrare, de a le prelucra şi de a le afişa ca rezultate. Aceste lucruri nu sunt posibile decât dacă datele pot fi "memorate". Cea mai folosită cale de a memora şi identifica date este aceea de a folosi variabile. O variabilă este o zonă identificabilă de memorie a cărei valoare poate fi modificată prin ac iunile algoritmului. Caracteristicile variabilelor sunt • identificarea de obicei realizată printr-un nume (format dintr-o înşiruire de litere şi cifre). Numele unei variabile dacă există se numeşte identificator; • tip de date. Este indicat ca să se cunoască ce fel de date vor fi memorate de către variabilă. Unele limbaje de programare (în special interpretoarele) admit existen a unui tip de date variabil adică variabila poate în perioada sa de defini ie să-şi modifice tipul. • zonă de memorie care va con ine valoarea la un moment dat a variabilei. Dimensiunea rezervată pentru o variabilă este de obicei în func ie de tipul de dată pe care îl con ine. Localizarea exactă a zonei de memorie este de obicei cunoscută doar în momentul execu iei programului. Variabila îşi poate schimba valoarea pe parcursul parcurgerii algoritmului, dar la un moment dat o variabilă nu poate avea decât o singură valoare. Valoarea variabilei poate fi utilizata de către algoritm prin intermediul numelui: oriunde apare numele unei variabile se în elege de fapt valoarea pe care aceasta o are la momentul respectiv. Prin tip de date se în elege gruparea datelor asemănătoare ca domeniu de defini ie sub un nume numit identificator de tip de date. În general, tipurile de date utilizate în informatica sunt submul imi ale mul imilor cunoscute din matematica. Limitarea plajei de valori este datorată caracterului finit al zonei de memorie folosite memorare. De exemplu, în limbajul C++ sunt utilizate, printre altele, următoarele tipuri: int: valori întregi intre -32767 şi 32768 (memorare pe 2octe i); long int: valori întregi intre -2147483648 şi 2147483647 (memorare pe 4octe i) Obs. Compilatoarele C moderne memorează tipul int pe 4 octe i, iar un întreg cu semn reprezentat pe 2 octe i se numeşte short int. Unii autori considera inclusă în defini ia tipului de date şi mul imea operatorilor şi func iilor care se pot aplica elementelor tipului respectiv. De exemplu, pentru un tip întreg, exista opera ii specifice: împăr irea întreagă şi modul aritmetic (restul împăr irii întregi). Prin constantă în elegem o valoare fixă, care nu se modifică de-a lungul aplicării unui algoritm. Constantele se reprezintă uzual printr-o valoare efectiva: 3, -2, -123, 9452 sunt constante întregi 3.456, 2.000, 3.4000E+10 sunt constante reale 'a', 'B', '&' sunt constante de tip caracter 2

'ecua ia are o infinitate de solu ii !' este o constanta şir de caractere

Modalită i de reprezentare a algoritmilor
Nu Da C

6. Blocul de decizie Nu C Da

Schema logică
Cea mai utilizata metoda de scriere a algoritmilor, cel pu in pentru începătorii în programare, este aceea a utilizării schemelor logice, care descriu opera iile ce pot fi aplicate datelor prin simboluri grafice numite blocuri. Sunt utilizate următoarele blocuri: START 1.Blocul START: Este blocul care indica începutul oricărei scheme logice şi este folosit o singura dată (orice schema logica are un început unic). 2.Blocul STOP Este blocul care încheie schema logica şi este, de asemenea, unic.

Blocul de decizie evaluează valoarea de adevăr a condi iei C şi, dacă aceasta este adevărata se trece pe ramura DA, iar dacă este falsă pe ramura NU. 6. Conectori Conectarea blocurilor ce formează o schema logica se realizează cu ajutorul unor săge i, acestea indicând şi sensul de efectuare a opera iilor ce compun schema logică. De asemenea, pentru a putea reuni mai multe săge i se folosesc cercule e. Cercule ele mici fără text încorporat au rolul de a uni estetic mai multe linii (conectori). Cercule ele mai mari având încorporate de obicei o literă din alfabetul grecesc pot avea rolul de a indica o continuare a schemei logice pe o altă pagină sau se folosesc pentru a elimina săge ile lungi inestetice.

STOP

3.Blocul de citire Blocul de citire corespunde opera iei de introducere a datelor, în timpul căreia se aşteaptă ca utilizatorul să introducă valori concrete, ce vor fi stocate în zona de memorie alocata variabilelor prezente după comanda CITEŞTE. Cu alte cuvinte, pentru fiecare variabila al cărui CITEŞTE nume este precizat în blocul de citire se cere introducerea unei valori. 4. Blocul de scriere Blocul de scriere corespunde operatei de afişare a valorii unei variabile sau a unui mesaj. După comanda SCRIE se precizează o lista cu numele variabilelor a căror valoare va fi afişată şi a mesajelor.

α

α

Scheme logice structurate
Schema logică poate fi definită ca o succesiune de blocuri de tipurile prezentate mai sus, cu următoarele caracteristici: a. con ine un singur bloc Start şi un singur bloc Stop; b. în fiecare bloc intra o singura săgeata şi, cu excep ia blocului de decizie, iese o singura săgeata (blocurile au o singura intrare şi o singura ieşire) c. exista cel pu in un “traseu” ce pleacă din blocul Start şi mergând în sensul săge ilor se ajunge în blocul Stop; secven a A1 Nu A2 C Da A Nu A2 C Da A1
Execu ie condi ionată simplă Execu ie condi ionată completă

SCRIE

5.Blocul de calcul variabila expresie Blocul de calcul corespunde opera iei numite atribuire, care are ca efect calcularea valorii expresiei şi memorarea valorii ob inute în zona de memorie alocata variabilei var. Schimbarea valorii unei variabile se poate realiza fie prin utilizarea unei atribuiri, fie prin citirea valorii prin intermediul unei opera ii de citire. Există şi un bloc de calcul de forma care indică o ac iune complexă (grafic indicat prin dublarea marginilor laterale), de obicei o procedură. Uzual informa iile din blocul de calcul sunt sub forma unor atribuiri variabila expresie care se traduc prin modificarea valorii variabilei cu valoarea dată de calculul expresiei.

d. blocul de decizie poate con ine, pe cele doua ramuri, orice combina ie de blocuri dintre cele prezentate; O schema logica ce are aceste proprietă i reprezintă un algoritm structurat.

Sunt posibile următoarele combina ii de opera ii ce descriu tipuri de scheme logice structurate: 1. Secven a înseamnă de fapt două sau mai multe ac iuni care se vor desfăşura una în continuarea celeilalte (adică ac iunea A2 se va realiza doar după încheierea ac iunii A1) 3

2. Execu ii condi ionate. Ele sunt de două feluri. Simplă, în cazul în care ac iunea apare doar pe ramura Da, respectiv completă, în cazul în care apar ac iuni pe ambele ramuri ale condi iei C. Condi ia simpla se exprimă prin: Dacă C atunci A sau în engleză If C then A iar cea completă se exprimă prin Dacă C atunci A1 altfel A2 sau în engleză If C then A1 else A2 Buclă cu Buclă cu 3. Bucle, care se folosesc pentru execu ia repetată test ini ial test final a unei ac iuni până în momentul în care condi ia devine falsă. Există două tipuri de bucle, cu test ini ial în care secven a este test-ac iune, si bucle cu test final în care secven a este de tip ac iune-test A Da C Bucla cu test ini ial se exprimă prin: atât timp cât C execută A Da A sau în engleză Nu C while C do A iar cea cu test final se Nu exprimă prin execută A atât timp cât C sau în engleză do A while C Să revenim pu in asupra opera iei de atribuire, opera ie care se efectuează într-un bloc de calcul şi care implică definirea mai multor termeni:

Prezentarea generală a no iunii de limbaj de programare
Am văzut în sec iunea precedenta cum pot fi reprezenta i algoritmii prin utilizarea pseudocodului sau a schemelor logice, insă prelucrarea automata a datelor presupune scrierea algoritmului intr-o forma ce poate fi în eleasă de calculatorul electronic. Algoritmii vor fi scrişi intr-un “limbaj de programare”, care va con ine opera ii asemănătoare celor despre care am amintit, numite acum instruc iuni. Limbajul de programare con ine: ALFABETUL: o mul ime de simboluri pentru scrierea cuvintelor din limbaj. Majoritatea limbajelor de programare folosesc alfabetul latin specific limbii engleze, cifre arabe, simboluri matematice etc. organizate sub forma unei codificări ASCII. Codificarea ASCII este de fapt o echivalarea a fiecărui simbol uzual folosit cu o valoare numerică (ini ial în intervalul [0, 127]). În elegerea acestei codificări de altfel intens folosită nu numai în programare este fundamentală în a în elege modul în care se memorează datele în calculator.

VOCABULARUL (LEXICUL): mul ime de cuvinte acceptate ca făcând parte din limbaj Se numeşte UNITATE LEXICALA cea mai mica "îmbinare" de caractere din vocabular, care are un în eles. Exista un set de reguli privind combinarea unită ilor lexicale în cuvinte şi a cuvintelor în "fraze" (reguli de SINTAXA), respectarea regulilor ducând la ob inerea unor construc ii corecte. SEMANTICA unui limbaj se refera la în elesul structurilor ob inute prin combinarea cuvintelor acceptate de limbaj. În vocabular avem: cuvinte cheie = cuvinte sau prescurtări ale unor cuvinte din limba engleza, ce reprezintă elemente de limbaj participând la crearea unor construc ii sintactic corecte (de exemplu comenzi sau instruc iuni). Cuvintele cheie sunt tratate diferit de restul textului din program de aceea ele trebuie cunoscute şi folosite doar în modul şi locul în care ele au fost definite. identificatori = nume folosite pentru variabile, tipuri de date şi func ii definite de utilizator. Un identificator este format dintr-un şir de caractere care începe cu o litera şi poate con ine litere, cifre şi caracterul "_" (underscore). Identificatorul, în func ie de limbajul în care este folosit nu poate con ine în general spatii sau apostrof, virgula, ghilimele ... Identificatorii nu pot coincide cu cuvintele cheie. Prin PROGRAM se în elege o succesiune de comenzi(instruc iuni) de prelucrare a datelor, scrise intr-un limbaj de programare. Programul este memorat intr-o entitate numita fişier sursa (este un fişier text). Prelucrările dintr-un program C++ sunt grupate în FUNC II. Rezolvarea unei probleme se face prin utilizarea unor func ii definite în limbaj şi / sau a unor func ii scrise de programator, atunci când func iile deja existente nu sunt suficiente. Func iile pe care limbajul le pune la dispozi ia utilizatorului sunt grupate, după tipul de prelucrare oferit, în mai multe fişiere numite "biblioteci" (fişiere HEADER). Pentru a putea utiliza o func ie trebuie să se specifice la începutul programului numele bibliotecii care con ine func ia respectiva. Orice program C++ trebuie să con ină o func ie numita "main" (un fel de “program principal”), instruc iunile con inute de aceasta fiind cele prelucrate atunci când programul este lansat în execu ie. Pentru a se putea ob ine rezultatele prelucrării datelor cu ajutorul programelor, trebuiesc parcurse următoarele faze: • scrierea programului (editarea textului sursa); • compilarea programului (=verificarea corectitudinii sintactice şi semantice a textului sursa şi prelucrarea sa fişier obiect) • editarea legăturilor (fişierul / fişierele obiect ob inute în urma compilării sunt transformate intr-un fişier executabil, adică într-un fişier care poate fi lansat în execu ie prin simpla scriere a numelui sau la prompt-ul sistemului de operare; Numim mediu de programare un program care permite asistarea programatorului în toate fazele de elaborare a unui program, scris intr-un limbaj de programare (editare, depanare, compilare, execu ie). În paragraful următor vor fi prezentate elementele de baza ale limbajului C. 4

Prezentarea generala a limbajului C++
Alfabetul
Alfabetul limbajului este format din acele simboluri utilizate la reprezentarea entită ilor unui program, adică a unitarilor lexicale. Reamintim ca, prin unită i lexicale în elegem cele mai mici entită i cu valoare semantica (adică au o semnifica ie), prin combinarea cărora rezulta construc iile sintactice ("propozi ii şi fraze"). Alfabetul limbajului C se compune din următoarele categorii de simboluri: • Literele mari şi mici ale alfabetului englez şi caracterul de subliniere "_" (eng. underscore) • Cifrele arabe: 0-9 • Semne de punctua ie: ; , ‘ " • Alte caractere: +, -, *, / (, ), {, }, [, ], \, ~, ^, <, >, =, ?, !, #, &, Literele şi cifrele, precum şi caracterul underscore, de multe ori asimilat în mul imea literelor, sunt utilizate pentru construirea identificatorilor şi cuvintelor cheie, după reguli ce vor fi descrise în paragrafele corespunzătoare. În limbajul C se face diferen a dintre literele mici şi majusculele corespunzătoare, deci identificatorul "a" va fi diferit de identificatorul "A".

Lista cuvintelor cheie ale limbajului C depinde de tipul de compilator folosit pentru mediul Borland C++ este: auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while Identificatorii defini i de utilizator nu trebuie să coincidă cu cuvintele rezervate. În limbajul C++ se mai adaugă câteva cuvinte cheie, care vor fi descrise la momentul oportun (în capitolul rezervat programării orientate obiect).

Comentarii
Comentariile sunt acele şiruri de caractere utilizate la explicarea programelor sursa, delimitate prin caractere speciale care determina ignorarea lor de către compilator. Un comentariu este o construc ie de forma /* … */ ca de exemplu: /* şir de caractere */ sau de forma // … ca de exemplu: // şir de caractere comentariu până la sfârşit de linie unde prin şir de caractere se în elege o secven a de caractere din setul caracterelor reprezentabile, mai pu in combina ia */. Nu se admit comentariile imbricate (adică un comentariu în alt comentariu). Comentariul poate fi scris pe mai multe linii dacă este scris în prima formă. A doua formă este specifică pentru C++ şi permite scrierea unor comentarii ce nu depăşesc o linie.

Identificatori
Identificatorul reprezintă nume pe care le atribuim variabilelor, constantelor, func iilor, tipurilor de date definite de utilizator. Un identificator este o secven a de litere, cifre şi caracterul underscore, primul caracter trebuind să fie litera sau underscore. Folosi i cu multa precau ie identificatori care încep cu underscore, pentru a nu intra în conflict cu numele rutinelor sistem, a căror ortografiere nu se cunoaşte (numele rutinelor sistem încep întotdeauna cu "_"). Regulile de formare a identificatorilor sunt aceleaşi cu regulile din Pascal. Un identificator poate avea, teoretic, o lungime arbitrara, dar în general doar primele 31 de caractere sunt luate în considerare de compilator (iarăşi depinde de compilator). Compilatorul de C face distinc ie între literele mici şi cele mari din numele identificatorilor (eng case sensitive) astfel că identificatorii următori sunt to i diferi i între ei: nume, Nume, NuME, NUMe Exemple (virgula are rol de separare): i, j, abc, tablouDeCaractere, tablou_de_caractere, _, _1,

Tipuri de date
Defini ia no iunii de tip de dată cuprinde, pe lângă mul imea de valori a tipului, şi alte aspecte: • dimensiunea memoriei alocate • mul imea opera iilor ce ac ionează asupra elementelor tipului • timpul de viata asociat datei, dat de clasa de memorie Aceste tipuri de date le vom folosi în continuare deoarece acesta defineşte o foarte importantă proprietate a constantelor cât şi a variabilelor folosite în programare. Tipurile de date sunt: • tipuri de baza: tipuri predefinite în limbaj; o caracter (char) - memorează un singur caracter scris în codificare ASCII (memorare pe 1octet) – pentru C tipul char poate fi de asemenea asimilat unui tip întreg de dimensiunea unui octet. o diferite variante de tipuri întregi întreg (int), care vor memora date întregi având de la valori în intervalul [32767, 32768] (memorare pe 2octe i) până la valori memorate pe 4 octe i.

Cuvinte rezervate (eng. keywords)
Numele rezervate instruc iunilor, tipurilor predefinite şi sintaxei de definire a func iilor şi tipurilor de date se numesc cuvinte cheie.

5

o

• o o o o o o

real (float, double, long double) folosite pentru a memora valori ra ionale ân limite foate largi de reprezentare, unde precizia (numărul de cifre posibil de reprezentat( devine de asemenea importantă. tipuri derivate: tipuri definite de utilizator; enumerare referin a structurate: tablou structura uniune

unsigned int
long long int signed long signed long int unsigned long unsigned long int

depinde implementare 2 sau 4 octe i 4 Octe i

de

întreg fără semn complement de 2 fa ă

[0, 65535] sau [0, 4294967295]
[UINT_MIN, UINT_MAX]

[-2147483648,2147483647]
[LONG_MIN , LONG_MAX]

4 Octe i

întreg fără semn

[0,4294967295]
[ULONG_MIN, ULONG_MAX]

Tipuri de date predefinite
Tipurile de baza definite în limbajul C sunt: întreg, real şi caracter. Exista insa mai multe tipuri întregi şi reale, în func ie de numărul de bi i pe care se pot memora valorile şi dacă valorile sunt cu sau fără semn. În plus, unele dintre aceste tipuri diferă de la o implementare la alta a limbajului (în func ie de calculatorul pe care se lucrează). În tabelul următor sunt prezentate tipurile fundamentale, memoria necesara stocării valorilor de acel tip şi limita valorilor ce pot fi memorate intr-o variabilă de acel tip.

Primul tip este tipul caracter. O variabila de acest tip va avea ca valoare codul ASCII asociat caracterului respectiv. De exemplu, dacă unei variabile i se atribuie caracterul ’a’, variabila va con ine valoarea 97 (numărul de ordine al caracterului ’a’ în codul ASCII). Tipul întreg cel mai des utilizat în programe este int, dar numărul de Byte (octe i) pe care se memorează valorile de acest tip diferă de la o implementare la alta a limbajului C (în func ie de tipul calculatorului). Astfel, tipul int este echivalent cu short int sau long int, în func ie de implementare. Celelalte tipuri întregi sunt ob inute prin folosirea prefixului signed sau unsigned, indicând dacă valorile respective sunt cu semn (con in şi valori negative), respectiv fără semn ( valori naturale). Exemplu: Un program C pentru a identifica tipurile de date folosite de compilatorul curent
#include <stdio.h> #include <limits.h> volatile int char_min = CHAR_MIN; int main(void){ printf("\n\n\n\n\n Tipuri Caracter\n"); printf("Numărul de biti pt un caracter: %d\n", CHAR_BIT); printf("Dimensiunea caracterului este: %d byte\n", int)sizeof(char)); printf("Signed char: [%d,%d]\n", SCHAR_MIN, SCHAR_MAX); printf("Unsigned char: [0,%u]\n", (unsigned int)UCHAR_MAX); printf("Implicit char este: "); if (char_min < 0) printf("signed\n"); else if (char_min == 0) printf("unsigned\n"); else printf("non-standard\n"); printf("\n Tipuri short\n"); printf("Dimensiune short int: %d bytes\n", (int)sizeof(short)); printf("Signed short min: [%d,%d]\n",SHRT_MIN, SHRT_MAX); printf("Unsigned short min: [0,%d]\n", (unsigned int)USHRT_MAX); printf("\n Tipuri Int \n"); printf("Dimensiunea pentru int: %d bytes\n", (int)sizeof(int));

Tipuri de date întregi
Tipul
char

Tipuri întregi de date Dimensiune Tip Limita valorilor definită mai memorie reprezentare clar de <limits.h> 1 Octet (Byte) Complement fa ă [-128,127] sau [0, 255]
de 2 sau întreg fără semn. Identifică un caracter ASCII [CHAR_MIN,CHAR_MAX]

signed char unsigned char int

1 Octet (Byte) 1 Octet depinde de implementare (pentru compilatoarele vechi 2octe i pentru cele moderne 4octe i) 2 Octe i 2 Octe i 2 Octe i întreg fără semn complement de 2 fa ă

[-128,127] sau
[SCHAR_MIN,SCHAR_MAX]

[0, 255] sau
[UCHAR_MIN,UCHAR_MAX]

[-32768,32767] sau [-2147483648,2147483647]
[INT_MIN , INT_MAX]

short int signed short int unsigned int short

complement fa ă de 2 complement fa ă de 2 întreg fără semn

[-32768,32767] sau
[SHRT_MIN, SHRT_MAX]

[-32768,32767] sau
[SHRT_MIN, SHRT_MAX]

[0, 65535] sau
[USHRT_MIN, USHRT_MAX]

6

printf("Signed int: [%d,%d]\n",INT_MIN, INT_MAX); printf("Unsigned int [0,%u]\n",(unsigned int)UINT_MAX); printf("\n Tipuri Long Int\n"); printf("Dimensiunea pentru long int: %d bytes\n", (int)sizeof(long)); printf("Signed long: [%ld,%ld]\n",LONG_MIN, LONG_MAX); printf("Unsigned long: [0,%lu]\n",ULONG_MAX); return 0; }

Exemple: pentru simplitate voi considera o reprezentare a datelor pe un octet (altfel spus pe 8 bi i) cu semn. O reprezentare a datelor pe 16bi i, 32 de bi i etc. se realizează simulat cu cea descrisă de exemplele mele numai că numărul de bi i diferă.

Valoare de reprezentat

Echivalent Hexazecimal Octal de la 00 la ff de la 000 la 377

Reprezentare 00 0 0 0 0 0 0 00 0 0 0 0 0 1 00 0 0 1 0 1 0 01 1 1 1 1 1 1 11 1 1 1 1 1 1

Observa ii 0 este un număr pozitiv deoarece are bitul semn 0 Valorile se cadrează la dreapta cea mai mare valoare pozitivă Se ob ine scăzând 1 din reprezentarea lui 0 0 0 0 0 0 0 0 0000 0 0 0 0 1 111 1 1 1 1 1

Din tabel se observa ca nu exista un tip logic, aşa cum este el definit în alte limbaje de programare. În limbajul Pascal, tipul logic (BOOLEAN) e definit ca mul imea cu valorile {False, True} împreuna cu operatorii logici cunoscu i (şi, sau , nega ia logica). În limbajul C nu s-a definit acest tip dar se foloseşte următoarea conven ie: o expresie este considerată adevărată dacă valoarea ob inuta la evaluarea ei este nenulă (are o valoare diferită de 0 exprimată prin valoarea 1) şi falsă (are valoarea 0) în caz contrar. Variabilele care se vor folosi ca variabile logice vor fi declarate de tip întreg. Tipul char se foloseşte în C pentru a identifica fie o valoare de reprezentare a unui caracter ASCII fie pentru a reprezenta o valoare întreagă cu un domeniu de valori foarte mic (256 valori diferite reprezentate pe 1Byte sau Octet).Tipul char diferă de tipul caracter din Pascal (unde caracterele nu se „amestecă” cu valorile întregi), în C caracterele fiind de fapt întregi, care desemnează codul ASCII corespunzător.

0 1 10 127 -1

00 01 0a 7f Ff

000 001 012 177 377

Complement fa ă de 2
Complementul fa ă de 2 reprezintă o modalitate foarte folosită pentru a reprezenta valori cu semn în memorie. Pentru a face o reprezentare a unor numere cu semn în memoria calculatorului trebuie să pornim de la următoarele premize: • în calculator se poate reprezenta un număr finit de valori într-o zonă de memorie de dimensiune prestabilită. Regula este că într-un spa iu de memorie având x bu i vom putea reprezenta 2x (2 la puterea x) valori distincte. Astfel pe 8 bi i reprezentăm 28=256 valori, pe 16bi i 216=65536 valori etc. • dacă vom dori reprezentarea unor date cu semn atunci trebuie să alocăm jumătate din spa iul de reprezentat pentru valorile pozitive şi jumătate pentru cele negative • semnul de reprezentare (pozitiv sau negativ) trebuie să fie cunoscut • calculele realizate cu valorile memorate trebuie sa fie cât mai simple. Modalitatea de reprezentare a datelor cu semn la care s-a ajuns şi este foarte cunoscută se numeşte complement fa ă de 2. Această modalitate de memorare foloseşte următoarele reguli: • primul bit din reprezentare se numeşte bitul de semn şi prin conven ie bitul de semn are valoarea 0 pentru numere pozitive şi 1 pentru cele negative • valorile pozitive sunt memorate ca şi valori direct ob inute ca transformări în baza 2 a valorilor date cadrate la dreapta şi completate cu zerouri nesemnificative în partea stângă • valorile negative sunt ob inute din cele pozitive fiind rezultatul scăderii valorii pozitive din reprezentarea valorii zero (vezi exemplele de mai jos).

-128 80 200 1 0 0 0 0 0 0 0 Avantajul reprezentării în complement fa ă de doi a valorilor întregi cu semn este acela că opera iile aritmetice pentru valorile cu semn fa ă de cele fără semn sunt identice (abstrac ie făcând de faptul că apar „depăşiri” de reprezentare prin transporturi dincolo de prima cifră a reprezentării).

Reprezentarea celei mai mici valori negative

Tipuri de date reale
Tipuri reale de date conform standardului IEEE 754 (mantisă şi exponent) Tipul Dimensiune Limite de reprezentare Precizie în cifre memorie semnificative float 4 Octe i [1.175494*10-38 , 3.402823*10+38] 7 double 8 Octe i [2.225073*10-308, 1.7976931*10+308] 15 long double 10 Octe i [3.37*10-4932, 1.18*10+4932] 19 Ultimele trei tipuri din tabel sunt tipuri reale, diferen a dintre ele constând în cantitatea de memorie utilizata, intervalul de valori şi precizia memorării valorilor (numărul de zecimale re inute). Valorile reale se reprezintă conform nota iei din standardul IEEE 754 (folosind semnul, mantisa şi exponentul). De exemplu, pentru tipul float se utilizează reprezentarea în simpla precizie, folosind un bit de semn, 7 bi i pentru exponent şi 24 de bi i pentru mantisa. Aceasta reprezentare are un exponent în limita a 10-37 şi 1038 cu 6 - 7 zecimale precizie. Pentru mai multe detalii referitoare la modul de memorare a datelor reale consulta i subcapitolul constante reale.

7

Tipul de date void
O alta caracteristica aparte o constituie introducerea tipului void, pentru a desemna “nimic” sau “orice tip”, în func ie de contextul în care este utilizat. În exemplele de până acum tipul void a fost folosit pentru a indica faptul ca func ia main nu întoarce nici o valoare.

Constanta întreagă
12 12u 12ul

Tip constantă zecimală cu semn zecimală fără semn Zecimală fără semn zecimală cu semn

Tip dată
short int unsigned short int unsigned long int long int (deoarece depăşeşte reprezentar ea de 16bi i) unsigned long int unsigned short int eronat

Reprezentare binară
00 0 0 0 0 0 0 0 0 0 0 1 1 0 0 00 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 00 0 0 00 0 0 00 0 0 00 0 0 00 0 0 00 0 0 01 1 0 00 0 0 00 0 0 00 0 0 00 0 0 00 1 1 00 0 0 11 0 1 01 0 0 00

Constante
O constantă este (de fapt ca şi o variabilă) o zonă de memorie care are un tip şi o valoare. Spre deosebire de variabilă valoarea unei constante nu se poate modifica în timpul execu iei programului. Tipul constantei ca şi valoarea sunt determinate de modul în care această constantă a fost scrisă. Există mai multe tipuri de constante dintre care amintim: • Constante întregi • Constante flotante • Constante caracter • Constante şir de caractere

50000

50000u -13 -13u

zecimală fără semn zecimală cu semn eronat deoarece nu se pot reprezenta numere negative eronat deoarece 9 nu este o cifră octală Octală fără semn eronat deoarece nu se pot reprezenta numere negative hexazecimală fără semn hexazecimală fără semn hexazecimală fără semn eronat deoarece nu se pot reprezenta hexazecimal numere negative

0 0 00 0 0 00 0 0 00 0 0 00 1 1 00 0 0 11 0 1 01 0 0 00 11 1 1 1 1 1 1 1 1 1 1 0 0 1 1

Constante întregi
Noi suntem obişnui i cu sistemul de numera ie zecimal, adică folosim zece simboluri prin care exprimăm valorile numerice (cifrele de la 0 la 9). Calculatorul, din motive tehnice lucrează în binar (foloseşte doar valorile 0 şi 1) pentru a reprezenta valorile. Valorile binare sunt destul de greu de memorat şi folosit, de aceea programatorii folosesc frecvent reprezentarea în octal (baza 8) sau hexazecimal (baza 16) a datelor. Limbajul C fiind mai apropiat ca alte limbaje de codul maşină şi fiind folosit pentru a descrie sisteme de operare permite utilizarea nu numai a valorilor scrise zecimal dar şi octal sau hexazecimal. O constantă întreagă zecimală cu semn se scrie în C fără nici un prefix sau sufix. Constantele sunt reprezentate fie pe 16 bi i (dacă valoarea variabilei este în intervalul de reprezentare a unei valori de tip short int adică [-32768,32767], sau reprezentare pe 32 de bi i dacă fie depăşeşte intervalul de definire pentru short int (dar valoarea ei este in intervalul unui long int [-2147483648, 2147483647]) sau valoarea este postfixată cu caracterul L sau l (de exemplu 12345678, 23l sau 123L). O constantă întreagă zecimală fără semn se scrie în C folosind sufixul u sau U. Constantele sunt reprezentate fie pe 16 bi i (dacă valoarea variabilei este în intervalul de reprezentare a unei valori de tip unsigned short int adică [0,65535], sau reprezentare pe 32 de bi i dacă fie depăşeşte intervalul de definire pentru unsigned short int (dar valoarea ei este in intervalul unui long int [0, 4294967295]) sau valoarea este postfixată de caracterele ul, lu, UL, sau LU (de exemplu 12345678u, 23ul sau 123LU). O constantă întreagă octală este o valoare fără semn scrisă în baza 8 (o succesiune de cifre de la 0 la 7) începând obligatoriu cu valoarea 0 (zero). De exemplu 0123 este o constantă octală iar 123 este o constantă zecimală. Ca şi constanta zecimală, constanta octală poate fi reprezentată pe 16bi i sau (dacă are o valoare mare sau este postfixată cu litera l sau L) pe 32de bi i. Exemple:

-192 012 -012

eronat unsigned short int eronat 00 0 0 0 0 0 0 0 0 0 0 1 0 1 0

0x12 0xaa2b 0XAbCdE -0x12

unsigned short int unsigned short int unsigned long int eronat

00 0 0 0 0 0 0 0 0 0 1 0 0 1 0 11 0 0 1 1 0 0 0 0 1 0 1 1 0 1 0 0 00 0 0 00 0 0 01 0 1 01 0 1 11 1 0 01 1 0 11 1 1 00

Constante flotante
O constantă flotantă se foloseşte pentru a reprezenta un număr ra ional şi este compusă din maxim trei componente plus eventual un indicator de tip: • parte întreagă – o constantă zecimală • parte frac ionară – un indicator separator de parte zecimală (reprezentat printr-un punct zecimal) urmat eventual de mai multe cifre zecimale • exponent – un indicator de exponent (litera e sau E), urmată de o valoare zecimală cu semn care reprezintă o putere a lui 10 la care este înmul ită valoarea. Oricare dintre cele trei păr i poate lipsi dar, la scrierea unei astfel de contante trebuie inut cont de faptul că obligatoriu trebuie să fie prezentă fie partea frac ionară, fie partea de exponent fie indicatorul lor. 8

În C exista trei tipuri de date reale (float, double şi long double), în consecin ă există posibilitatea de a folosi trei tipuri de constante reale: • constanta double – se reprezintă pe 64bi i, nu are sufix deci este implicită în C • constanta float – se reprezintă pe 32bi i şi are sufixul f sau F • constanta long double – se reprezintă pe 80 de bi i şi are sufixul l sau L

Reprezentarea datelor flotante conform IEEE 754-2008.
Tipul float Con ine 1. 1 bit de semn (0 reprezintă un număr pozitiv, 1 un număr negativ) 2. 8 bi i exponent (pentru a reprezenta exponen i negativi există o valoare (eng. bias) – pentru simplă precizie 127 – care trebuie scăzută din exponent pentru a indica valoarea exactă a exponentului. 3. 23 bi i pentru a memora partea zecimală normalizată a valorii de reprezentat (24 bi i precizie reală) această parte se mai numeşte şi mantisă. Precizia reală este dată de faptul că în mod implicit primul bit al unei reprezentări este totdeauna 1 (şi nu este direct reprezentat) Exemplu: 25 se reprezintă astfel: • Valoarea sa normalizată este 25(10)=11001(2) = 1,1001*24 • Deducem că: • Semnul 0 deoarece numărul este pozitiv • Exponentul este 127(bias) + 4 = 131 reprezentat ca 10000011 în baza 2 • Mantisa este 1,1001 din care cifra supraunitară nu se scrie deci 1001 deci în final numărul este: 0 10 0 000 1 110 0 100 0 000 0 000 0 000 0 000 0 Adică în hexazecimal 41c8 0000 Exemple: Valoare Constanta Zecimală flotantă 1 1.f -2
1/3 0 -0 +∞ -∞

11 bi i exponent (pentru a reprezenta exponen i negativi există o valoare – pentru dublă precizie 1023 (210-1) – care trebuie scăzută din exponent pentru a indica valoarea exactă a exponentului. 3. 52 bi i pentru a memora partea zecimală normalizată a valorii de reprezentat (53 bi i precizie reală) adică cca. 15 cifre zecimale. Tipul long double are o reprezentare pe 80 bi i. Modul de reprezentare con ine: 1. 1 bit de semn (0 reprezintă un număr pozitiv, 1 un număr negativ) 2. 15 bi i exponent (pentru a reprezenta exponen i negativi există o valoare – pentru precizie 214-1 – care trebuie scăzută din exponent pentru a indica valoarea exactă a exponentului. 3. 64 bi i pentru a memora partea zecimală normalizată a valorii de reprezentat (65 bi i precizie reală) adică cca. 19 cifre zecimale. Observa ii: 1. Cele trei reprezentări reale sunt destul de complicate dar de multe ori dorim sa folosim în calcul numere ra ionale deci utilizarea lor este pe deplin justificată. 2. Calculatoarele personale moderne folosesc procesoare ce realizează calcule în virgulă flotantă folosind o reprezentare a datelor pe 80 de bi i (deci precizie de long double).

2.

Constantă caracter
Există multe programe sau subprograme care sunt scrise cu scopul de a prelucra text. Unitatea fundamentală a textului este caracterul. Caracterul este o modalitate simplă şi elegantă de a reprezenta literele, cifrele sau alte simboluri care apar într-un text într-o formă codificată numeric prin aceasta simplificându-se mult prelucrarea şi memorarea acestuia. Cel mai intens folosi mod de a codifica literele şi celelalte simboluri din text este standardizat pentru limba engleză (americană) şi se numeşte ASCII. Din păcate această standardizare nu ine cont de simbolurile specifice altor ări (care nu folosesc literele engleze), există preocuparea la nivel interna ional de a realiza standarde interna ionale cere să includă şi caracterele acestor ări dar din păcate acest lucru este departe de a fi realizat la un nivel acceptabil de către toată lumea. Setul ASCII cuprinde 128 de caractere realizând o codificare ini ială pe 7 bi i. Pentru că marea majoritate a calculatoarelor reprezintă datele în multipli de 8 bi i (sau octe i sau Byte) putem spune că reprezentarea unui caracter (folosită de fapt şi de C este pe 8 bi i). Observa ii • Pentru informa ii suplimentare studia i anexa • Există unele versiuni foarte noi de C (în special folosite în mediul grafic) care recunosc şi folosesc o reprezentare pe 16 bi i a unui caracter dar aceste versiuni nu sunt dezbătute pe cuprinsul acestui curs. Caracterele ASCII sunt împăr ite în: • caractere grafice (adică acele caractere care pot fi reprezentate printr-un simbol grafic) intervalul [33,126] • precum şi caractere negrafice care cuprind valorile {[0,32], 127} O constantă caracter în C va avea valoarea corespunzătoare codului ASCII al caracterului ASCII pe care îl reprezintă. 9

Reprezentare hexazecimală 3f80 0000 c000 0000 7f7f ffff 3eaa aaab 0000 0000 8000 0000 7f80 0000 ff80 0000

Reprezentare binară
0 01 1 1 11 1 1 00 0 00 00 00 00 00 00 0 00 0 0 00 0 1 10 0 0 00 0 0 00 0 00 00 00 00 00 00 0 00 0 0 00 0 0 11 1 1 11 1 0 11 1 11 11 11 11 11 11 1 11 1 1 11 1 0 01 1 1 11 0 1 01 0 10 10 10 10 10 10 1 01 0 1 01 1 0 00 0 0 00 0 0 00 0 00 00 00 00 00 00 0 00 0 0 00 0 1 00 0 0 00 0 0 00 0 00 00 00 00 00 00 0 00 0 0 00 0 0 11 1 1 11 1 1 00 0 00 00 00 00 00 00 0 00 0 0 00 0 1 11 1 1 11 1 1 00 0 00 00 00 00 00 00 0 00 0 0 00 0

Observa ii
Mantisa are valoarea 0 Cea mai mare valoare Valoare rotunjită Caz special Ciudat dar posibil Plus infinit Minus infinit

-2.f
0.3333333f 0.f -0.f

≈3.4028234*1038 3.4028234e38f

Tipul double are o reprezentare pe 64 bi i. Modul de reprezentare con ine: 1. 1 bit de semn (0 reprezintă un număr pozitiv, 1 un număr negativ)

O constantă caracter va fi memorată ca un întreg (va avea tipul int). O constantă de tip caracter în C poate fi definită ca o construc ie lexicală incluse între două caractere ’(apostrof). Construc ia lexicală dintre apostroafe poate fi: • un caracter dacă acesta este un simbol grafic cu excep ia caracterului \ (backslash) sau ’ care sunt tratate special de către compilator • un caracter \ urmat de un alt caracter sau literă caz în care caracterul are o semnifica ie specială (vezi tabelul de mai jos). • una sau mai multe (maxim 3) cifre octale (numită secven ă escape) care sunt interpretate ca o valoare octală pentru a descrie un caracter din tabelul ASCII. O secven ă escape poate depăşi intervalul normal de definire a unei reprezentări ASCII ([0,127]) ajungând şi în intervalul [128,255] (o extindere pe 8 bi i a codului ASCII) astfel valoare poate fi în octal între 0 şi 377. Exemple Constantă Cod Cod Denumire Utilizare Caracter ASCII ASCII caracter zecimal octal ’\\’ 49 57 Backslash Pentru a putea scrie caracterul backslash ’\’’ 39 47 Apostrof Pentru a putea crea o constantă caracter cu valoarea apostrof ’\”’ 34 42 Ghilimele Pentru a putea crea o constantă caracter cu valoarea ghilimele ’a’ 97 141 a Litera a minusculă ’A’ 65 101 A Litera A majusculă ’0’ 48 60 0 Cifra 0 (zero) ’1’ 41 61 1 Cifra 1 (unu) ’\a’ 7 7 Bell Calculatorul generează un sunet ’\b’ 8 10 Backspace Un spa iu înapoi ’\t’ 9 11 HT Tabulator orizontal ’\n’ 10 12 LF Trecere la linie nouă (în C echivalent cu trecere la rând nou) ’\v’ 11 13 VT Tabulator vertical – foarte rar folosit ’\f’ 12 14 FF Salt la pagină nouă (la imprimantă) ’\r’ 13 15 CR Retur de car – revenire la începutul rândului curent ’\7’ 7 7 Bell Ca mai sus la Bell ’ \101’ 65 101 A Litera A majusculă ’ \377’ 255 377 ??? Cea mai mare valoare ASCII posibil de reprezentat nu se ştie caracterul reprezentat deoarece depinde de la un SO la altul ’ \401’ Eroare valoare imposibil de reprezentat (nu există în tabela ASCII ’ \182’ Eroare – e nevoie de cifre octale iar 8 nu e una dintre ele

Constantă şir de caractere
O succesiune de zero au mai multe caractere care sunt incluse între ghilimele formează o constantă şir de caractere sau mai pe scurt un şir de caractere. Fiecare dintre caracterele ce compun şirul poate fi un caracter ca şi cele descrise la constantă caracter. Practic limbajul C va descrie şirul de caractere ca un tablou de caractere care con ine toate caracterele scrise în şir precum şi caracterul ’\0’ (supranumit caracterul null) care in C indică terminarea şirului de caractere. Exemple:
Şir de caractere

”” ”a” ”123” ”\123” ”\1234”

ASCII NUL a NUL 1 2 3 NUL S NUL S 4 NUL

Reprezentare Hexazecimal 00 61 00 41 42 43 00 53 00 53 44 00

Octal 000 141 000 061 062 063 000 123 000 123 063 000

Byte 1 2 4 2 3

Observa ii Şir nul de caractere

”ab\0cd”

N N a bUc dU L L

61 62 00 63 64 00

141 142 000 143 144 000

6

Cele 3 cifre sunt interpretate ca o valoare ASCII octal Cele 3 cifre sunt interpretate ca o valoare ASCII octal a 4-a ramâne 4 Deşi se alocă 6 octe i pentru şir doar primele 2 caractere vor fi vizibile, practic echivalent cu ”ab”

Constante predefinite
În C există anumi i identificatori care de fapt sunt asimila i unor constante. Un exemplu tipic este PI cunoscut din matematică dar sunt multe astfel de cazuri ca de exemplu CHAR_MIN şi CHAR_MAX ş.a. care au fost deja definite. Aceşti identificatori sunt asimila i unor constante dar de fapt modul de descriere şi implementare este total diferit astfel încât amănunte despre ele voi da în capitolul directive precompilator.

Variabile
O variabilă este o zonă identificabilă de memorie, modificabilă prin opera ii de atribuire care are un tip şi o valoare. Spre deosebire de variabilă valoarea unei constante nu se poate modifica în timpul execu iei programului. În C orice variabilă vom folosi trebuie declarată. Pentru început vom spune că toate variabilele vor avea un nume şi anume un identificator folosit atât la declarare cât şi la utilizarea lor. Acest fapt nu este întotdeauna adevărat dar deocamdată ideea că variabilele au un nume ne este de foarte mare ajutor în folosirea lor. Limbajul C este unul complicat astfel încât este bine de ştiut faptul că numele unei variabile nu se păstrează în mod normal în faza de execu ie a programului. Variabilele se pot clasifica după modul în care se memorează datele în: 10

• •

variabile simple – care pot memora o singură valoare. Variabilele care folosesc tipuri de date predefinite sunt în general simple. variabile compuse – care memorează mai multe valori de o dată. De multe ori se doreşte gruparea mai multor date care au atât separat cât şi împreună semnifica ie. Limbajul C oferă mai multe metode prin care se pot grupa datele, cele mai importante tipuri de date complexe fiind tablourile şi structurile.

Exemple de tablouri unidimensionale cu sau fără ini ializări: int x[10]; double tablou[25]; char sir[20]=”Primul program C”; int prime[10]={1, 2, 3, 5, 7, 11, 13, 17, 19, 23} int primePeste100[]={101, 103, 107, 109}

Variabile simple
Pentru a putea utiliza o variabila intr-un program C, este obligatorie declararea acesteia, astfel: [clasa_de_memorie]TIP_DE_DATE lista_variabile; unde: • clasa_de_memorie specifica locul unde se va aloca memorie pentru variabila şi durata sa de viata, no iuni la care vom reveni mai târziu). Specificarea clasei de memorie este op ionala; • TIP_DE_DATE este orice tip predefinit sau derivat; practic tipul variabilei va indica şi dimensiunea de memorie alocată pentru variabila respectivă. • lista_variabile con ine lista variabilelor de acel tip, despăr ite prin virgula; Efectul unei declara ii este crearea unei zone de memorie ca loca ie a variabilei, identificarea zonei de memorie cu numele dat variabilei şi eventual ini ializarea variabilei (adică atribuirea unei valori ini iale implicite sau explicite variabilei). Exemple: int a,b,c; char ch; float x,y; long int z; int g=7; Observa ie: La declararea variabilei întregi g s-a realizat şi ini ializarea acesteia (adică stabilirea unei valori ini iale). Variabilele care sunt declarate în interiorul corpului unei func ii se numesc variabile locale, iar cele declarate în afara oricărei func ii din program se numesc variabile globale. Variabilele globale vor putea fi utilizate în toate func iile ce compun programul, iar variabilele locale doar în cadrul func iei în care au fost definite, dar aceste amănunte vor fi discutate mai târziu.

Indicii tablourilor
Pentru a ne referi la un element al unui tablou în C se folosesc indicii. Dacă la definirea unui tablou valoarea dintre parantezele dreptunghiulare reprezintă numărul de elemente pe care le va avea un tablou, apoi în timpul execu iei programului elementele unui tablou se identifică prin tablou[indice], unde tablou înseamnă numele tabloului iar indice al câtelea element din tablou (începând cu elementul 0 în limbajul C). Indicele unui tablou este expresia care apare între paranteze drepte după numele tabloului. Exemple pentru defini ia: int prime[10]={1, 2, 3, 5, 7, 11, 13, 17, 19, 23} prime[0] are valoarea 1 prime[1] are valoarea 2 prime[2] are valoarea 3 prime[3] are valoarea 5 ş.a. pentru defini ia char sir[20]=”Primul program C”; sir[0] are valoarea ’P’ sir[15] are valoarea ’C’ sir[16] are valoarea ’\0’ sau null În C se pot folosi şi construc ii mai ciudate ca de exemplu se poate folosi indicele pentru o constantă şir de caractere ca în exemplele de mai jos: Construc ia ”123456789”[0] este echivalentă cu ’1’ iar ”123456789”[9] este echivalentă cu ’\n’.

Tablouri multidimensionale
Este posibilă declararea şi folosirea unor tablouri care au două sau mai multe dimensiuni numite tablouri multidimensionale, un exemplu cunoscut este acela al matricelor care sunt tablouri ce au două dimensiuni. Aceste tablouri pentru a fi controlate au nevoie de mai mul i indici, de exemplu pentru matrici primul indice se foloseşte pentru a arăta liniile unei matrici iar coloanele sunt indicate de al doilea indice. Exemple: double matrice [4] [5]; defineşte o matrice cu 4 linii şi 5 coloane iar matrice [2] [4] ne indică elementul de pe linia 3 şi coloana 5 a matricei cu numele matrice.

Variabile compuse. Tablouri.
Un tablou este o mul ime ordonată de elemente de acelaşi tip la care ne putem referi folosind indici. Un tablou unidimensional este echivalentul în programare a unui şir din matematică. Spre deosebire de şirurile din matematică în programare şirurile sunt obligatoriu de dimensiune limitată iar pentru programele scrise în C sau alte limbaje compilate dimensiunea acestor tablouri trebuie să fie cunoscută în momentul declarării lor şi ea rămâne fixă pe toată durata execu iei programului.

11

Operatori, operanzi, expresii
O expresie constă din unul sau mai mul i operanzi lega i prin operatori.

Operand
În C mai multe construc ii lexicale pot avea rol de operand. Printre altele un operand poate fi: • constantă • variabilă simplă • nume de tablou • nume de structură • numele unui tip de dată • numele unei func ii • un element al unei structuri fiecărui operand al unei expresii are două proprietă i interesante pentru a calcula o expresie şi anume o valoare prin care participă la calculul expresiei şi un tip prin care compilatorul determină tipul rezultat în urma calculului.

Operatori
Operatorii constituie unul din conceptele cele mai importante şi mai interesante care stau la baza unui limbaj de programare. Limbajul C este vestit pentru marea varietate de operatori pe care-i pune la dispozi ia programatorului rezultând şi o diversitate de expresii ce se pot forma pe baza acestora. După cum se ştie, o expresie este formata din variabile, constante, func ii şi operatori. În acest paragraf voi aminti câteva din categoriile de operatori ai limbajului C:

Operatorii aritmetici:
+ adunare, - scădere, * produs, / împăr ire, % - restul împăr irii întregi ++, -- incrementare şi decrementare

Operatori
Operatorul reprezintă o opera ie care se aplica datelor. Limbajul C este vestit pentru marea varietate de operatori pe care-i pune la dispozi ia programatorului rezultând şi o diversitate de expresii ce se pot forma pe baza acestora. După cum se ştie, o expresie este formata din variabile, constante, func ii şi operatori. În acest paragraf voi aminti câteva din categoriile de operatori ai limbajului C: Există foarte mul i operatori în C. motivul pentru care avem o varietate mare de operatori este acela că C a fost conceput ca un limbaj care sa permită folosirea cât mai bună a codului maşină (generat de program) şi a ob ine un program executabil cât mai rapid cu putin ă (cerin e foarte importante pentru componentele unui sistem de operare). Unii operatori sunt foarte rar găsi i în alte limbaje de programare, al ii sunt trata i diferit de alte limbaje de programare. Există mai multe moduri prin care putem organiza aceşti operatori. Dintre aceste moduri amintim: • după numărul de operanzi • operatori unari – au un singur operand Operatorul unar poate fi plasat în dreapta sau stânga operandului iar pentru diferite paranteze (care sunt considerate de c tot ca operatori) atât în stânga cât şi în dreapta operandului • operatori binari – au doi operanzi, primul fiind plasat în stânga operatorului iar celălalt operand în dreapta • operator ternar – are trei operanzi. C are un astfel de operator şi anume ?: unde operanzii sunt organiza i sub forma: operand ? operand : operand • ordinea de execu ie a opera iile • de la stânga la dreapta – o ordine normală şi obişnuită din matematică care se aplică la majoritatea • de la dreapta la stânga – o ordine care pare anormală şi neobişnuită dar destul de logică la o analiză mai atentă 12

Expresia
poate fi definita astfel: Variabilele şi constantele sunt expresii Dacă E1 este expresie atunci op_unar E1 este expresie; (operator prefixat) Dacă E1 este expresie atunci E1 op_unar este expresie; (operator postfixat) Dacă E1, E2 sunt expresii, atunci E1 op_binar E2 este expresie; Dacă E1, E2 , E3 sunt expresii, atunci E1 op_ternar1 E2 op_ternar2 E3 este expresie; (f) Orice expresie corecta se ob ine prin aplicarea paşilor (a)...(e) de un număr finit de ori; Exemple de expresii: 23.4+12/4-a*b+1, c && d || e && f, 1234, -765.34 Evaluarea unei expresii = ob inerea unei valori în func ie de valorile operanzilor ce apar în expresie. Dacă expresia este numerica, atunci se ob ine o valoare numerica, dacă expresia con ine operatori logici şi rela ionali atunci se ob ine o valoare logica (adevărat sau fals). În C (sau C++) nu există de fapt tipul de date logice ci este valabilă următoarea constatare: - Orice valoare numerică egală cu 0(zero) se consideră falsă. - Orice valoare numerică diferită de 0(zero) se consideră adevărată. (dacă în urma unei evaluări de expresie este nevoie de calcularea unei expresii care returnează adevărat atunci de fapt valoarea calculată este 1(unu)). Exemple: 1 < 3 este adevărat iar acest adevăr este reprezentat prin valoarea 1. 2 > 7 este fals deci este reprezentat prin valoarea 0. (a) (b) (c) (d) (e)

prioritatea operatorilor • dacă doi operatori (sau mai mul i) operatori sunt folosi i natural este ca ordinea în care aceştia vor fi aplica i să fi semnificativă de exemplu pentru cazul 2+3*4 pot fi două ordini de efectuare a opera iilor astfel (2+3)*4=20 sau 2+(3*4)=14. În matematică (dar şi in limbajul C) dacă nu există paranteze atunci înmul irea va avea prioritate mai mare decât adunarea. În continuare vă voi prezenta un tabel în care vor apărea pentru fiecare dintre operatorii C toate aceste informa ii de care am vorbit mai sus. Nu to i operatorii vor fi descrişi în acest capitol. Motivul este că unii dintre operatori sunt încă prea complica i pentru acest capitol dar vor fi descrişi în capitolele următoare sau se reperă la programarea obiectuală care nu face obiectul acestei căr i. Tabel cu operatorii specifici C++ Nivel Operator Tip Descriere ordine 1 :: Unar Operatorul scop folosit Stânga Dreapta pentru a identifica elemente ale unei clase 2 () [] . -> ++ -- const ++ Unar Pentru ++ respectiv – Stânga Dreapta -- ~ ! sizeof new delete operatorul poate fi atât postfixat cât şi prefixat 3 *& Unar Indirectare şi referin ă Dreapta Stânga pentru (pointeri) +Unar Operator de semn unar Dreapta Stânga 4 (tip) Unar Operatorul de for are a Dreapta Stânga tipului (eng. casting) 5 .* ->* Binar Pointer la membru Stânga Dreapta 6 */% Binar Operatori aritmetici Stânga Dreapta multiplicativi 7 +Binar Operatori aritmetici de Stânga Dreapta adunare 8 << >> Binar Operatori de deplasare Stânga Dreapta (eng. shift) 9 < > <= >= Binar Operatori rela ionali Stânga Dreapta 10 == != Binar Test de egalitate sau Stânga Dreapta inegalitate 11 & Binar Operatorul Şi (eng. AND) Stânga Dreapta pe bi i 12 ^ Binar Operatorul Sau exclusiv Stânga Dreapta (eng. XOR) pe bi i 13 | Binar Operatorul Sau (eng. OR) Stânga Dreapta pe bi i 14 && Binar Operatorul logic Şi (eng. Stânga Dreapta AND) 15 || Binar Operatorul logic Sau Stânga Dreapta (eng. OR) •

16 17

?: Ternar Operatorul condi ional Dreapta Stânga = *= /= %= += -= >>= Binar Operatorii de atribuire Dreapta Stânga <<= &= ^= != 18 , Binar Operatorul virgulă Stânga Dreapta În continuare voi prezenta operatorii nu în ordinea din tabel ci grupa i după felul opera iilor pe care le execută.

Operatori aritmetici
Operatorii aritmetici sunt cunoscu i din matematică şi de aceea ei vor fi prezenta i primii aceştia sunt: Nivel Operator Tip Descriere ordine 3 +Unar Operator de semn unar Dreapta Stânga 6 */% Binar Operatori aritmetici Stânga Dreapta multiplicativi 7 +Binar Operatori aritmetici de Stânga Dreapta adunare 18 , Binar Operatorul virgulă Stânga Dreapta Obs. Operatorii enumera i mai sus pot fi utiliza i în limbajul C şi C++; dacă ve i folosi alte limbaje uni dintre aceşti operatori pot diferi ca forma de cei prezenta i de aceea este indicată studierea documenta iilor pentru a vedea diferen ele de nota ie. Limbajul C++ defineşte o mul ime de al i operatori, care vor fi studia i la momentul oportun. Atribuirea se desfăşoară astfel: (a) Se evaluează expresia din partea dreapta a atribuirii; (b) Valoarea ob inută în urma evaluării se memorează în zona de memorie a variabilei cu numele specificat în partea stângă a atribuirii. Obs: Vechea valoare a variabilei se pierde în momentul efectuării unei opera ii de atribuire. Fie următoarele atribuiri: i=3; i=i+1; Ne punem întrebarea dacă a doua atribuie este corecta. Din punct de vedere matematic, a doua atribuire este o absurditate însă, din punct de vedere al programării este corecta şi se realizează astfel: (a) a. Se evaluează expresia i+1, iar rezultatul ob inut este 4 (vechea valoare a lui I, 3, la care se adaugă o unitate); (b) b. Se stochează în i valoarea 4; Exerci iul 1 : Ce valoare are variabila x după următoarea secven a de atribuiri: x=3; y=5; x=x+y; y=2*y; x=x-y; Exerci iul 2: (Bacalaureat 1999) Se da următoarea secven a de atribuiri: 13

a=10; b=4; a=a-b; b=a+b; a=b-a; (a) Ce valori au variabilele a şi b ? (b) Ce efect au ultimele trei atribuiri ? Teme: Construi i schema logica pentru rezolvarea ecua iei de gradul al II-lea.

Dacă valoarea ob inuta prin evaluarea expresiei din dreapta atribuirii este de alt tip decât tipul variabilei din stânga atunci se încearcă conversia tipului valorii la tipul variabilei. Nu întotdeauna conversia este posibila, caz în care se va afişa un mesaj de eroare în urma compilării. Exemplul 1 : Fie următoarea secven a de program: void main(void){ int a; float c; a=3./2.+9./4.; cout<<”a=”<<a<<”\n”; c=3./2.+9./4.; cout<<”c=”<<c<<”\n” /* aceeaşi expresie are valori diferite, în func ie de tipul variabilei din stânga atribuirii */ } Observa ie: Executând programul de mai sus se vor ob ine valori diferite pentru variabilele a şi c, deşi expresia care apare în dreapta ambelor atribuiri este aceeaşi. Pentru a se ob ine valoarea 3, iar pentru c se ob ine 3,75. Explica ia sta în modul în care se realizează conversiile de tip în cadrul atribuirilor. Valoarea expresiei 3./2.+9./4. este 3.75 dar variabilei a i se va atribui partea întreaga a acestei valori, adică 3. Variabila c este de tip float (număr real), deci nu va mai fi nevoie de conversia rezultatului evaluării expresiei la tipul variabilei. Exemplul 2: void main(void){ float a; a=5/2; cout<<a; } Observa ie: Executând acest program se va afişa valoarea 2 şi nu valoarea 2.5 aşa cum ar fi fost de aşteptat. Explica ia este următoarea: operatorul / realizează, atunci când operanzii sunt întregi, împăr irea întreaga, deci vom ob ine catul împăr irii întregi dintre 5 şi 2. Exista doua modalită i de rezolvare a problemei: fie înlocuim expresia cu 5./2. (5.=5.0) fie utilizam operatorul de conversie explicita ( ). Acest nou operator ( numit cast), se utilizează astfel ( tip_de_date) expresie; Semnifica ia este următoarea: Se cere convertirea valorii rezultate din evaluarea expresiei la o valoare de tipul specificat intre paranteze. În exemplul de mai sus vom înlocui atribuirea cu: a=(float) 5/2; (rezultatul expresiei va fi de tip float, deci variabilei a i se va atribui valoarea 2.5). Exerci iu: Fie următorul program, care calculează media aritmetica a trei numere a,b,c , primele doua introduse de la tastatura.:

Operatori rela ionali:
<,> == egal <= mai mic sau egal >= mai mare sau egal != diferit

Operatori logici:
&& - şi logic || - sau logic ! - nega ie logica Operatorii logici şi rela ionali sunt implica i în formarea expresiilor cu valoare logica. Spre exemplu: a<2, 7/5+2<s-1, (x>-3) &&(x<3), !(a==b) sunt expresii cu valoare logica.

Operatori de atribuire.
În sec iunea precedenta am amintit, vorbind despre reprezentarea algoritmilor prin scheme logice, de opera ia de atribuire. În limbajul C se considera ca atribuirea este un operator. Atribuirea are următoarea sintaxa: variabila = expresie; Modul de func ionare este următorul: 1. se evaluează expresia; 2. valoarea ob inuta este stocata în zona de memorie a variabilei, realizându-se eventuale conversii de tip. 3. valoarea rezultata în urma atribuirii este valoarea atribuita variabilei. Observa ie: Atribuirea fiind un operator, se considera ca operanzii sunt variabila din partea stânga, respectiv expresia din partea dreapta. Orice expresie trebuie să aibă, în urma evaluării sale, o valoare (un rezultat). Valoarea expresiei de atribuire este valoarea ob inuta prin evaluarea expresiei din partea dreapta a atribuirii.

14

void main(void){ int a,b,c; float s; cin>>a; cin>>b; c=a/2+b/3; s=(a+b+c)/3; cout<<”Media aritmetica este=”<<s; } Executa i acest program şi identifica i greşelile. Corecta i greşelile găsite.

operatori de atribuire compusa:
+=, -=, *=, /=, %= Operatorii de atribuire compusa au fost introduşi pentru a fi utiliza i în locul atribuirilor de tipul: v=v+expresie; v=v-expresie; v=v*expresie; etc. unde: • v - variabila • expresie-orice expresie corecta în limbajul C În locul acestor expresii se vor folosi: v+=expresie; v -= expresie; etc. Folosirea operatorilor de atribuire compusă sporeşte lizibilitatea programului şi viteza de execu ie. Dacă atribuirea este de forma: v=v+1; sau v=v-1; se pot utiliza operatorii de incrementare şi decrementare ++ şi --. Expresiile de mai sus se vor putea scrie: v++; respectiv v--; Exemplul 3: Se da următorul program:

void main(void){ int a,b,i=3; a=++i; //preincrementare a lui i cout<<a; b=a--; //postdecrementare cout<<”a=”<<a<<”\n”; cout<<”b=”<<b<<”\n”; a+=++b; //atribuire compusa cout<<”a=”<<a<<”\n”; cout<<”b=”<<b<<”\n”; } Observa ie: Când apar expresii de tipul : ++variabila sau --variabila; spunem ca se realizează o preincrementare, respectiv predecrementare. În cazul preincrementării, variabila va primi ca valoare 1+valoarea ini ială. Dacă preincrementarea apare intr-o expresie se realizează întâi aceasta opera ie şi abia apoi celelalte opera ii care mai apar în expresie (preincrementarea are prioritate mai mare). În atribuirea a = ++i se realizează întâi preincrementarea, în urma căreia i va avea valoarea 4, şi abia apoi se realizează atribuirea (a va avea valoarea 4). În expresia b=a — se realizează o postdecrementare, adică se modifica valoarea variabilei a doar după ce s-au efectuat celelalte opera ii din expresie. În cazul nostru, b va primi valoarea lui a (4, în momentul acela) şi abia apoi se va micşora valoarea lui a cu o unitate. Exerci iu: Executa i programul de mai sus şi urmări i rezultatele care se ob in. Mai exista operatori de atribuire compusa, folosindu-se operatorii logici ce ac ionează la nivel de bit, dar despre aceştia vom vorbi intr-o sec iune viitoare. Tot atunci vom discuta şi despre atribuirea multipla.

Citirea şi afişarea datelor. Primele programe în C++
Pentru introducerea datelor prin intermediul tastaturii avem nevoie de func ia cin, iar pentru afişarea valorilor sau mesajelor de func ia cout. Exemplul 1: Cel mai simplu program este programul care nu realizează nici o prelucrare: void main (void) { } Exemplul 2: Să se afişeze pe ecran un mesaj.

15

#include <iostream.h> void main (void) { cout<< "Acesta este primul program in C!"; } Observa ii: 1. Linia #include <iostream.h> declara ca se va utiliza biblioteca de func ii cu numele "iostream.h", din care face parte şi func ia de afişare cout. 2. Func ia main() este func ia principala a unui program C, prelucrările con inute de aceasta fiind primele efectuate la executarea unui program. 3. Prelucrările ce apar intr-o func ie trebuiesc scrise intre { şi } 4. După scrierea unei comenzi (func ie / instruc iune) se pune ; Exemplul 3: Reluăm exemplul precedent: #include <iostream.h> void main(void){ cout<< "Acesta este primul program în C "; cout<<"\n scris în prima ora de laborator "; } Observa ii: • Caracterul "\n" are ca efect afişarea a ceea ce urmează pe linia următoare a ecranului (adică salt la rândul următor). • Mesajul trebuie precedat de << (aceste semne trebuie în elese ca o direc ie a fluxului de date de la textul scris spre func ia care afişează textul pe ecran) Exemplul 4: Să se scrie un program pentru însumarea a doua numere. #include <iostream.h> void main(void){ int a; //variabila care va memora primul număr int b; //variabila care va memora al doilea număr int s; // memoreaza suma celor doua numere cout <<"Introduce i primul număr:"; cin >> a; cout <<"Introduce i al doilea număr:"; cin >>b; s=a+b; cout<<"Suma "<<a<<"+"<<b<<"="<<c<<"\n"; } Observa ii: • Se folosesc trei variabile pentru a memora numerele şi suma acestora. Prin "int a" se declara folosirea unei variabile de tip întreg, având numele "a". • "cin >> a" determina citirea unei valori de la tastatura, valoare ce va fi memorata în zona de memorie a variabilei "a". Cel care introduce date trebuie să tasteze o valoare întreagă, altfel semnalându-se eroare. • Dacă se afişează un mesaj, con inutul acestuia trebuie pus intre ghilimele. Dacă se doreşte afişarea valorii unei variabile atunci se scrie, după simbolurile <<, numele acesteia.

Executând programul de mai sus ve i observa modul de afişare al rezultatului. În textul programului au fost introduse mici comentarii, precedate de //, pentru a facilita în elegerea textului sursa. Exemplul 5: Scrie i un program care să afişeze următorul meniu: Băuturi Bere ………..12500 Vin …………34764 Suc…………...5000 Cafea Nes….10000 Expreso………6000 #include <iostream.h> void main(void){ cout <<”Bauturi:\n”; cout <<”\t Bere………………12500\n”; cout <<”\t Vin………………..34764\n”; cout <<”\t Suc…………………5000\n”; cout <<”Cafea:\n”; cout <<”\t Nes……………….10000\n”; cout <<”\t Expreso……………6000\n”; } Observa ii: S-a folosit caracterul \t pentru a se “începe” un nou paragraf. Caracterele “\t” şi “\n” fac parte dintre caracterele numite caractere escape, deoarece sunt caractere neafişabile pe ecran decât prin utilizarea unor simboluri (secven e de evitare). • •

Instruc iunea alternativa (if)
Până acum am folosit ca exemple programe simple, care con ineau doar func iile de intrare / ieşire şi operatorul de atribuire. Atunci când elaboram programe ceva mai complexe este necesar ca, intr-un anumit punct al programului, să decidem continuarea acestuia în func ie de o condi ie. În primul laborator am dat ca exemplu de algoritm rezolvarea ecua iei de gradul 1 (pseudocod şi schema logica). Intr-o schema logica ni se permitea utilizarea unui bloc de decizie de următorul tip: NU C A B DA

cu semnifica ia: dacă este adevărata condi ia se executa prelucrarea B, altfel se executa prelucrarea A. În limbajul C este utilizata instruc iunea if, cu următoarea sintaxă: 16

if (expresie) instruc iune1; [else instruc iune2;} unde: • expresie - orice expresie valida în limbajul C • instruc iune1, instruc iune2 - orice instruc iune corecta din limbajul C ; Mod de func ionare: • se evaluează expresia • dacă expresia este ne nulă (conven ia pentru adevărat) se executa instruc iune1 • dacă expresia este nula (falsă) se executa instruc iune2 • se trece la următoarea instruc iune a programului Observa ie: alternativa else instruc iune2 poate să lipsească. Exemplul 4: Să se scrie un program pentru calcularea maximului a doua numere. #include <iostream.h> void main(void){ int a,b; int max; cout<<”a=”; cin>>a; cout<<”\nb=”; cin>>b; if (a<=b) max=b; else max=a; cout<<”Maximul celor doua numere este=”<<max<<”\n”; } Exemplul 5: Scrie i un program care calculează maximul a 3 numere.

#include <iostream.h> void main (void){ int a,b,c; int max; cout<<”a=”; cin>>a; cout<<”\nb=”; cin>>b; cout<<”\nc=”; cin>>c; if (a<=b) if (c<=b) max=b; else max=c; else if (c<=a) max=a; else max=c; cout<<”\nMaximul este=”<<max; } Observa ie: După cum se vede din exemplu, instruc iune1 şi instruc iune2 pot fi de asemenea instruc iuni if. Spunem ca sunt instruc iuni if imbricate. A se observa şi modul cum a fost scris programul, astfel încât să se vadă clar cărei instruc iuni if ii apar ine fiecare else. Exemplul 6: Se dau trei numere întregi a,b,c. Să se testeze dacă numerele date pot fi lungimile laturilor unui triunghi.

17

#include <iostream.h> void main (void){ int a,b,c; cout<<”a=”; cin>>a; cout<<”\nb=”; cin>>b; cout<<”\nc=”; cin>>c; if (a<b+c) if (b<a+c) if c<a+b) cout<<” Numerele pot fi lungimile laturilor unui triunghi !”; else cout<<”Numerele nu pot fi lungimile laturilor unui triunghi !”; else cout<<”Numerele nu pot fi lungimile laturilor unui triunghi !”; else cout<<”Numerele nu pot fi lungimile laturilor unui triunghi !”; } Observa ie: Nu am mai verificat dacă a, b, c sunt pozitive. Exemplul 7. Reluăm exemplul precedent cu inten ia de a folosi mai pu ine instruc iuni if. #include <iostream.h> void main (void) { int a,b,c; cout<<”a=”; cin>>a; cout<<”\nb=”; cin>>b; cout<<”\nc=”; cin>>c; if ((a<b+c) && (b<a+c) && (c<a+b)) cout<<”Numerele pot fi lungimile laturilor unui triunghi !”; else cout<<”Numerele nu pot fi lungimile laturilor unui triunghi !”; } Observa ie: Am folosit o singura instruc iune if, cele trei condi ii anterioare fiind conectate prin operatorul ŞI logic. După cum se ştie, dacă avem propozi iile p,q,r, atunci propozi ia p&&q&&r va fi adevărata dacă toate propozi iile componente sunt adevărate şi falsă dacă una dintre propozi iile componente este falsă.

Este indicata aceasta scriere pentru ca sporeşte lizibilitatea programului. Exemplul 8: Rezolvarea ecua iei de gradul I. #include <iostream.h> void main(void) { int a,b; float x; cout<<”\na=”; cin>>a; cout<<”\nb=”; cin>>b; if (a!=0){ x=(float) -b/a; cout<<”\nx=”<<x; } else if (b==0) cout<<”\n x apar ine lui R “; else cout <<”\n Ecua ia nu are solu ii”; } Observa ie: În cazul în care a este nenul avem de realizat doua opera ii: atribuirea valorii -b/a variabilei x şi afişarea valorii rădăcinii. Atunci când se realizează mai multe opera ii acestea trebuiesc incluse intre { şi }.

Instruc iuni iterative
Toate problemele prezentate până acum au putut fi rezolvate folosind numai func ii de citire / scriere, atribuiri şi instruc iunea de decizie (if). Cele mai multe dintre probleme vor necesita structuri de date mai complexe precum şi folosirea unor noi instruc iuni, care să permită repetarea de un număr oarecare de ori a unor par i din algoritm. Să luam ca exemplu algoritmul de calcul al sumei a 2 numere introduse de la tastatura. El consta în citirea valorilor pentru cele doua numere şi afişarea sumei acestora. Nu era nevoie decât de doua variabile, cate una pentru fiecare număr. Acest exemplu este doar unul didactic, în practica ne întâlnindu-se prea des cazuri în care să fie nevoie de a suma doua numere. Generalizarea problemei pentru n numere va modifica substan ial algoritmul nostru. Nu vom putea folosi cate o variabila pentru fiecare număr introdus deoarece nu cunoaştem exact cate numere avem (n este introdus de utilizatorul programului, altfel algoritmul nu ar mai fi general). Chiar dacă s-ar şti ca avem, să zicem, 2500 de numere ar fi cam dificil să utilizam 2500 de variabile distincte. Problema noastră se poate rezolva simplu dacă utilizam o instruc iune iterativa (ciclica), care să ne permită repetarea de n ori a următoarei secven e de opera ii: 1. citim o valoare folosind variabila “a”; 2. adunam valoarea citita la rezultatul par ial, memorat în variabila “s”; Instruc iunile ce descriu aceste prelucrări ciclice sunt compuse din doua păr i: • corpul ciclului, format din prelucrările ce se doresc a fi realizate de mai multe ori; 18

• condi ia, pe baza căreia se stabileşte dacă se vor mai executa prelucrările din ciclu (este obligatorie executarea prelucrărilor din corpul instruc iunii de un număr finit de ori); Corpul ciclului sau condi ia trebuie să con ină acele opera ii care, după efectuarea de un număr de ori a corpului instruc iunii, să determine schimbarea valorii de adevăr a condi iei, permi ând încheierea executării instruc iunii ciclice şi trecerea la următoarele instruc iuni ale algoritmului. În cazul în care este neglijat acest aspect, programul se va executa la infinit. Condi ia trebuie să fie o expresie cu valoare logica. Reamintesc faptul ca în limbajul C++ nu exista un tip de date logic, dar se utilizează conven ia ca orice valoare ne nula să fie considerata ca adevăr iar valoarea zero ca valoarea fals. În consecin ă, orice expresie cu valoare de tip întreg va putea fi utilizata drept condi ie intr-o instruc iune iterativa. În finalul paragrafului o chestiune de terminologie: o execu ie a corpului instruc iunii poarta numele se itera ie. Instruc iunile iterative pot fi clasificate, în func ie de momentul evaluării condi iei, astfel: • cicluri cu test ini ial (While şi For)- evaluarea condi iei se face înaintea fiecărei itera ii; • cicluri cu test final (Do .. While)- evaluarea condi iei se face la sfârşitul fiecărei itera ii;

#include <iostream.h> void main() { float a; int i,n; float suma=0; i=1; cout<<"\n Numărul de elemente="; cin>>n; while(i<=n){ cout<<"Elementul "<<i<<"este: "; cin>>a; suma=suma+a; //se mai poate scrie suma+=a i++; } cout<<"Suma este= "<<suma; } Exemplul 2: Suma primelor n numere naturale. #include <iostream.h> void main() { int n; int i; int suma=0; cout<<"n="; cin>>n; i=1; while(i<=n){ suma=suma+i; i++; } cout<<"Suma este: "<<suma; } Observa ie: Se poate utiliza o scriere mai compacta, care elimina şi necesitatea utilizării variabilei i: while (n--) suma+=n; Modul de execu ie este următorul: • valoarea condi iei este valoarea variabilei n, valoare care scade cu o unitate după fiecare itera ie (postdecrementare) • condi ia devine falsă atunci când valoarea lui n devine zero;

Instruc iunea While
Instruc iunea while (“atât timp cat”) este o instruc iune iterativa cu test ini ial şi are următoarea sintaxa: while ( <condi ie>) instruc iune; unde: <condi ie> - orice expresie cu valoare întreaga; instruc iune - orice instruc iune valida a limbajului; Mod de func ionare: • dacă expresia este adevărată se executa prelucrările din ciclu; • altfel, se trece la următoarea instruc iune de după while; Cu alte cuvinte, prelucrările din ciclu se executa atât timp cat condi ia este adevărata. Dacă expresia este falsă de la început corpul ciclului nu se va executa deloc. Problema propusa în paragraful precedent se poate rezolva astfel: Exemplul 1 Suma a n numere introduse de utilizator

19

Instruc iunea do .. while
Instruc iunea do .. while este o instruc iune iterativa cu test final şi are următoarea sintaxa: do instruc iune; while (<condi ie>) Mod de func ionare: • se executa corpul instruc iunii; • se evaluează condi ia: dacă aceasta este adevărata se reia execu ia, altfel se trece la următoarea instruc iune din program; Deosebirea esen ială fa ă de instruc iunea while este aceea ca expresia se evaluează după itera ie. În cazul în care condi ia este falsă de la început, corpul instruc iunii se executa o singura dată. Utilizarea instruc iunii do .. while este mai pu in frecventa (se foloseşte pentru acele prelucrări care trebuiesc executate cel pu in o dată). Dacă rescriem exemplul 1 utilizând do .. while, ob inem: …………………………. do{ cin>>a; suma+=a; i++; } while(i<=n); ………………………….

Exemplul 3: Reluăm exemplul 1 folosind instruc iunea for: #include <iostream.h> void main(){ int i, n; float a, suma=0; cin>>n; for(i=1;i<=n;i++){ cin>>a; suma+=a; } cout<<”suma =”<<suma; } Observa ii: Variabila i este utilizata pe post de “contor” al instruc iunii for, numărând la a câta itera ie s-a ajuns. • Execu ia instruc iunii for se încheie atunci când numărul de itera ii devine egal cu n. Ini ializarea lui i cu 1 se realizează o singura dată, la început; i <= n este condi ia de continuare a execu iei; i++ se efectuează după fiecare execu ie a ciclului (postincrementare). • Aceasta forma a instruc iunii for este cea mai utilizata. Un alt mod de a descrie execu ia acesteia este următorul: pentru i de la 1 la n se executa corpul instruc iunii. • Nu este obligatoriu ca valoarea lui i să se mărească de fiecare dată cu o unitate. • Nu este obligatorie utilizarea tuturor celor trei expresii din for, dar este obligatorie scrierea separatorului “;” . Exemplul de mai sus se poate rescrie astfel: …………………………… for( ;n--; ){ cin>>a; suma+=a; } …………………………… •

Instruc iunea for
Una dintre cele mai puternice instruc iuni iterative ale limbajului C (şi nu numai) este instruc iunea for, care are următoarea sintaxa: for (expresie1; expresie2; expresie3) [instruc iune]; unde: expresie1, expresie2, expresie3 - expresii valide în C++; instruc iune - orice instruc iune a limbajului C++; Parantezele pătrate semnifica faptul ca instruc iunea este op ionala; Mod de execu ie: • expresiile au următoarea semnifica ie generala: • expresie1 - expresie de ini ializare; • expresie2 - condi ia de continuare a execu iei ciclului; • expresie3 - expresie de actualizare; • atât timp cat expresie2 este adevărata se executa corpul ciclului; • de fiecare dată se evaluează expresia de actualizare, care are rolul esen ial de a determina ca, după un număr de itera ii, expresie2 să devină falsă; • expresie1 se evaluează o singura dată;

No iunea de tablou
În laboratorul precedent explicam de ce nu putem să utilizam 2500 de variabile distincte atunci când dorim să însumam 2500 de numere. Rezolvarea propusa atunci se baza pe citirea repetata a cate unei valori, folosind o variabila a, urmata de adăugarea acesteia la suma par iala. Dezavantajul metodei este acela ca nu se pot memora toate valorile citite ci doar ultima (citind o valoare intr-o variabila, vechea valoare a acesteia se pierde). Cele mai multe programe presupun prelucrări complexe asupra datelor de intrare, deci va fi nevoie să memoram şirul nostru de numere astfel încât să poată fi utilizat şi în alte prelucrări, nu numai pentru calcularea sumei. 20

Pentru a rezolva astfel de situa ii s-a introdus în limbajele de programare no iunea de tablou. Tablourile ne permit memorarea unui număr mare de valori utilizând o singura variabila. Prin tablou se în elege un număr de elemente de acelaşi tip, numit tip de baza, stocate intro zona compacta de memorie. Un tablou poate fi definit astfel: tip_de_baza nume [dimensiune1] [dimensiune2] … [dimensiune_n]; unde: tip_de_baza = tipul elementelor tabloului; n = numărul de dimensiuni al tabloului; [dimensiune_i] = numărul de elemente pe dimensiunea i tabloul are dimensiune1*…*dimensiune_n elemente No iunea de tablou multidimensional poate fi în eleasă mai bine după parcurgerea no iunilor referitoare la vectori şi matrice.

#include <iostream.h> void main(){ int n; //numărul de elemente int a[50]; /* se defineşte o variabila de tip tablou cu maxim elemente, deci n va trebui sa fie mai mic decât 50 */ int min; //variabila ce va memora minimul int i; //contor în instruc iunea for

50

de

Tablouri unidimensionale
Declararea unui tablou unidimensional: tip_de_baza nume[dimensiune]; unde dimensiune specifica numărul de elemente al vectorului. De exemplu: int a[30]; declara un vector ce con ine 30 de elemente de tip int, float b[50]; declara un vector cu 50 elemente reale; “Numerotarea” elementelor se face de la 0 la dimensiune-1, adică cele 30 de elemente ale primului tablou sunt: a[0], a[1], a[2], …, a[29]. Spunem ca tabloul este o variabila indexata, deoarece fiecare element al tabloului poate fi “găsit” / “utilizat” cunoscând numărul sau de ordine. Vectorul este un şir de valori în care se cunoaşte precis care este primul, al doilea, ….,ultimul element. “Numărul de ordine” al unui element se numeşte indice. Exemplul 1: Să se calculeze minimul elementelor unui şir de n numere, utilizând vectori.

cout<<”n=”; cin>>n; /* se citesc elementele şirului */ for(i=0;i<n;i++){ cout<<”a[“<<i<<”]=”; cin>>a[i]; } // se calculează minimul min=a[0]; /*ini ializam minimul cu primul element din şir */ for(i=1;i<n;i++) if (a[i]<min) min=a[i]; cout<<”Minimul este=”<<min; //afişare minim } După cum se poate observa din programul de mai sus, fiecare element al şirului se poate utiliza ca şi cum ar fi o variabila de tip int independenta, deci valoarea unui element al vectorului poate fi modificata independent de celelalte elemente. Să presupunem ca utilizatorul introduce pentru n valoarea 4. Ini ial, elementele vectorului nu au o valoare bine definita; putem reprezenta grafic vectorul astfel: Să presupunem ca utilizatorul introduce valorile 3, 7, 2, 9 pentru cele patru elemente ale vectorului. Reprezentarea vectorului a va fi următoarea: a[0] a[1] a[2] a[3] 3 7 2 9 Valoarea oricărui element al vectorului poate fi modificata fie printr-o atribuire, fie prin introducerea unei valori (folosind func ia cin, de exemplu) de la tastatura. Dacă vom introduce atribuirea a[2]=23; valoarea elementului al treilea din vectorul a nu va mai fi 2 (vechea valoare) ci 23. De asemenea, dacă se scrie: cin>>a[2]; valoarea elementului va fi cea introdusa de utilizator de la tastatura. Exemplul 2: Se considera n numere întregi introduse de la tastatura. Să se afle cate numere sunt pare şi cate impare.

21

#include <iostream.h> #include <conio.h> void main() { int n; //numărul de elemente int v[30]; //vector maxim 30 întregi int pare, impare; // memorează nr. elementelor pare, impare int j; //variabila contor pentru for clrscr(); //citim numărul de elemente cout<<”n=”; cin>>n; /* se citesc elementele şirului */ for(i=0;i<n;i++) { cout<<”v[“<<i<<”]=”; cin>>v[i]; } //ini ializam variabilele pare=0; impare=0; //luam fiecare element din v şi testam dacă acesta este sau nu par for(j=0;j<n;j++) if (v[j] % 2 = =0) pare++; else impare++; //afişam rezultatul cout<<”Am găsit “<<pare<<” numere pare şi “<<impare; } În programul de mai sus verificam, pentru fiecare element, dacă acesta se împarte exact la doi, caz în care am mai descoperit un element par. În caz contrar, numărul elementelor impare se măreşte cu unu. Am utilizat operatorul % , numit şi modul aritmetic, care are ca rezultat restul împăr irii lui v[j] la 2. Exemplul 3: Să se introducă de la tastatura un cuvânt şi să se afişeze.

#include <iostream.h> #include <conio.h> void main(){ char cuvant[30]; clrscr(); cout<<”Introduceti cuvantul:”; cin>>cuvant; cout<<”Cuvantul introdus este:”<<cuvant; } Observa i cu aten ie cum am declarat variabila ce va memora cuvântul: folosim un vector cu elemente de tip caracter. Acest mod de declarare ne permite să lucrăm cu oricare dintre caracterele ce formează cuvântul. De exemplu, dacă utilizatorul introduce cuvântul salariu, vectorul nostru de caractere arata cam aşa: Dacă dorim să modificam pu in cuvântul putem scrie: cuvant[6]=’i’; şi vom ob ine cuvântul salarii. Exemplul 4: Să se citească un cuvânt şi să se găsească numărul de vocale pe care le con ine. #include <iostream.h> #include <conio.h> # include <string.h> void main(){ char c[30];// memorează cuvântului int vocale; // numărul vocalelor int j; clrscr(); cout<<”Introduceti cuvantul:”; cin>>c; vocale=0; //căutam vocalele for(j=0;j<strlen(c);j++) if ( c[j]==’a’ || c[j]==’e’ || c[j]==’i’ || c[j]==’o’ || c[j]==’u’) vocale++; cout<<”Am gasit “<<vocale<<” vocale.”; } • Observa ii: Am utilizat func ia strlen, care are ca rezultat numărul de caractere al şirului dat ca parametru. Func ia poate fi utilizata doar dacă a fost inclus fişierul header string.h, fişier ce con ine func iile ce ac ionează asupra şirurilor de caractere.

22

“Mecanismul “algoritmului este următorul: se ia cate un caracter din şirul introdus şi se verifica dacă elementul respectiv con ine unul dintre caracterele a, e, i, o, u. De fiecare dată când condi ia este adevărata se incrementează numărul de vocale. Mai multe despre şirurile de caractere vom spune după capitolul dedicat pointerilor. Exemplul 5 (Căutare secven iala) Se considera un şir de n elemente întregi şi un număr întreg x. Să se verifice dacă numărul x face parte din şirul considerat.

#include <iostream.h> #include <conio.h> void main(){ int v[25]; int n; int x; int j; int gasit; //variabila logica ce indica dacă x apar ine lui v clrscr(); //citim numărul de elemente cout<<”n=”; cin>>n; /* se citesc elementele şirului */ for(j=0;j<n;j++){ cout<<”v[“<<j<<”]=”; cin>>v[j]; } //se citeşte elementul x cout<<”x=”; cin>>x; gasit=0; j=0; //căutam x în şirul v while( (j<n)&&(!gasit)){ if (v[j]==x) gasit=1; j++; } if(gasit) cout<<”Elementul apar ine şirului dat !”; else cout<<”Elementul nu apar ine şirului !”; } Am utilizat instruc iunea while, condi ia de efectuare a ciclului fiind (j<n) && (!gasit). Corpul instruc iunii while se va executa atât timp cat nu s-a ajuns la sfârşitul şirului şi elementul x nu a fost găsit în v. Condi ia de mai sus devine falsă în doua cazuri: fie am epuizat elementele şirului (j a ajuns egal cu n), fie am găsit un element egal cu x. După executarea ciclului while testam dacă variabila găsit este ne nula (adevăr), caz în care x apar ine lui v, altfel x nu apar ine şirului.

Exemplul 6. (sortare) Se considera un şir cu n elemente reale. Să se aranjeze elementele în ordine crescătoare utilizând acelaşi vector. #include <iostream.h> #include <conio.h> void main(){ double v[40]; int n; int j, k; cout<<”n=”; cin>>n; for(j=0;j<n;j++){ cout<<”v[“<<j<<”]=”; cin>>v[j]; } //sortarea crescătoare a şirului v for(j=0;j<n-1;j++) for(k=j+1; k<n;k++) if (v[j]>v[k]){ //interschimbăm valorile v[j] şi v[k] temp=v[j]; v[j]=v[k]; v[k]=temp; } //afişarea valorilor sortate crescător for(k=0;k<n;k++) cout<<v[k]<<”\t”; }

Tablouri bidimensionale (matrice)
Pe lângă vectori, cel mai utilizat tip de tablou de numere este tabloul bidimensional, numit de cele mai multe ori matrice. Declararea unei matrice se face astfel: tip_de_baza nume[dimensiune_1][dimensiune_2]; Exemplu: double a[10][5]; //tablou cu 10 linii şi 5 coloane de elemente reale int a[3][2]; //tablou cu 3 linii şi 2 coloane de elemente întregi Pentru a putea avea acces la valoarea unui element al matricei, trebuie să precizam linia şi coloana pe care se afla acesta. Un element poate fi specificat prin a[i][j], i - reprezentând linia şi j - coloana. În cazul vectorilor citeam o valoare reprezentând numărul de elemente. Pentru matrice vom citi o valoare m - numărul maxim de linii şi o valoare n - numărul maxim de coloane. O matrice cu m linii şi n coloane va avea m * n elemente. Dacă m = n atunci matricea se numeşte matrice pătrata de ordin n. Exemplul 7. Să se citească elementele unei matrice şi să se afişeze. 23

#include <iostream.h> #include <conio.h> void main(){ int a[20][20]; //matrice cu elemente întregi int m, n; // linii, respectiv coloane int i, j; //contoare pentru for //citim numărul de linii şi coloane cout<<”Numărul de linii:”; cin>>m; cout<<”Numărul de coloane:”; cin>>n; //se citesc elementele matricei for(i=0;i<m;i++) for(j=0;j<n;j++){ cout<<”a[“<<i<<”][“<<j<<”]=”; cin>>a[i][j]; } //afişarea elementelor matricei for(i=0;i<m;i++){ for(j=0;j<n;j++) cout<<a[i]][j]<<”\t”; cout<<”\n”; } } Exemplul 8. Se dau doua matrice cu m linii şi n coloane. Să se calculeze matricea suma. ………………………………………………. int a[20][20], b[20][20], c[20][20]; int m,n; int i, j; ……………………………………………….. //se citesc dimensiunile m, n şi cele doua matrici a şi b ………………………………………………. //calculam matricea suma for(i=0;i<m;i++) for(j=0;j<n;j++) c[i][j]=a[i][j]+b[i][j]; //se afişează matricea c ……………………………………………….. Atunci când lucrăm cu matrice avem nevoie de doi indici: i pentru a parcurge liniile matricei şi j pentru a parcurge coloanele acesteia. Pentru fiecare valoare a lui i, j ia toate valorile intre 0 şi n-1, deci se parcurge linia i. În ultimul exemplu am utilizat trei matrice. Pentru mai multa claritate, putem declara un tip ale cărui elemente să fie matrice:

typedef int matrice[20][20]; matrice a, b, c; Am definit un tip de date (tip de date definit de utilizator) ale cărui elemente sunt matrice cu maxim 20 de linii şi coloane cu elemente întregi. Numele noului tip este matrice. Declararea variabilelor de acest tip se poate face oriunde în programul în care apare defini ia. Definirea unui tip de date se poate face numai prin utilizarea cuvântului cheie typedef înaintea declara iei. Dacă definim mai multe tipuri se va folosi typedef pentru fiecare defini ie.

No iunea de pointer
Pointerii au fost introduşi în limbajele de programare pentru a putea rezolva mai eficient anumite probleme sau pentru a da mai multa claritate anumitor programe. O prima defini ie poate fi următoarea: Pointerul este o variabila ce con ine adresa unui obiect. Obiectul a cărei adresa este re inuta de pointer poate fi: variabila func ie Fie următorul exemplu: int x; int *px; Am definit o variabila de tip întreg x şi o variabila pointer, care poate con ine adresa unei variabile de tip întreg. Simbolul * ce apare În stânga variabilei px arata ca px este o variabila pointer. Prin atribuirea px=&x; Pointerul va avea ca valoare adresa de memorie alocata variabilei x (vezi laboratorul nr.1, defini ia variabilei). Operatorul unar & este utilizat pentru a se ob ine adresa variabilei x (operator unar = are un singur operand) Acum putem să lucrăm cu con inutul variabilei x (adică cu valoarea acesteia) prin intermediul pointerului px, deci indirect, fără să mai folosim variabila x. La prima vedere, aceasta modalitate de lucru poate părea dificila şi nu tocmai utila. Necesitatea utilizării pointerilor va apare cu mai multa claritate în sec iunea dedicata şirurilor de caractere şi func iilor. Exemplul 1. Fie programul următor:

24

#include <iostream.h> void main(){ int x,y; int *px; cout<<"x="; cin>>x; cout<<"y="; cin>>y; px=&x; cout<<"x are valoarea "<<*px; *px=y; cout<<"\nx a devenit "<<x; } În programul de mai sus am introdus valorile variabilelor întregi x şi y, am definit un pointer la variabila x şi am atribuit acestuia adresa de memorie alocat variabilei x. Să analizam atent linia: cout<<"x are valoarea "<<*px; Prin *px se în elege valoarea aflata în zona de memorie a cărei adresa este memorata în pointerul px. Valoarea afişată va fi chiar valoarea introdusa pentru x deoarece, înainte de afişare, pointerul px a primit ca valoare adresa variabilei x, adresa la care se afla valoarea acesteia (valoare dobândita prin utilizarea func iei cin). Atribuirea *px=y; va modifica valoarea care se afla la adresa memorata de px, valoare care va fi valoarea introdusa de utilizator pentru variabila y. Astfel va fi modificata chiar valoarea pe care o are variabila x. Fireşte ca era mai simplu să folosim atribuirea x=y; care are acelaşi efect şi ne scuteşte de de-a mai folosi pointeri, insa exemplul este pur didactic. Operatorul unar * este folosit sub forma *variabila_pointer, valoarea acestei expresii fiind valoarea care se găseşte în memorie la adresa memorata de pointerul ce apare ca operand. În concluzie, prin px avem acces la adresa variabilei x, iar prin *px la valoarea variabilei x. Vom spune ca un pointer “refera” indirect un obiect sau ca “pointează”(arata) la obiectul respectiv. Variabilele pointer pot fi încadrate ca fiind de tip referin a. Exemplul 2. Să se calculeze suma a doua numere reale folosind pointeri.

#include <iostream.h> void main(){ double x, y, z; double *px, *py, *pz; cin>>x; cin>>y; px=&x; py=&y; pz=&z; *pz=*px+*py; cout<<"Suma este: "<<*pz; }

Pointeri şi tablouri
În limbajul C, exista o foarte strânsa legătura intre pointeri şi tablouri, astfel ca pointerii şi tablourile sunt tratate la fel. Orice program în care apar tablouri poate fi modificat astfel încât să folosească pointeri în locul tablourilor. În aceasta sec iune vom discuta despre legătura dintre pointeri şi vectori (tablouri unidimensionale). Fie următoarele declara ii: int a[20]; int *pa; Am declarat o variabila a , care este un vector cu maxim 20 elemente întregi şi pa un pointer la o variabila de tip întreg. După cum se ştie, o valoare int are nevoie de 16 bi i pentru a fi memorata, adică 2 Byte (o variabila int poate retine numere întregi intre -32768 şi 32767, vezi curs Bazele Informaticii). Pentru tabloul a vor fi aloca i 2*20=40Byte consecutivi în memorie adică, pentru primul element a[0] sunt aloca i primii 2Byte, pentru a[1] următorii 2Byte,…, pentru a[19] ultimii 2 Byte din cei 40 aloca i. Fie atribuirea: pa=&a[0]; După aceasta atribuire, pointerul pa con ine adresa primului element al vectorului, adică pa pointează la începutul vectorului a. Dacă scriem pa=&a[3]; atunci pa va referi elementul al 4-lea din vectorul a, iar *pa va con ine valoarea sa. Opera iile care se pot realiza cu pointeri sunt: • compara ia • adunarea unui pointer cu un întreg • scăderea unui întreg dintr-un pointer Doi pointeri pot fi compara i folosind operatori rela ionali. În compara ia:

25

if(p1==p2) cout<<”Adrese identice”; else cout<<”Adrese diferite”; se verifica dacă adresa memorata de p1 este aceeaşi cu adresa re inuta de p2, unde p1 şi p2 sunt pointeri de acelaşi tip. Se poate compara un pointer cu valoarea NULL (sau 0). Un pointer are valoarea NULL (valoare nedefinita) dacă nu refera nici un obiect. Adunarea unui pointer cu un întreg este definita numai atunci când pointerul refera un tablou (un element al tabloului). Scăderea este definita în acelaşi caz. Exemplul 3. Să se citească elementele unui vector şi să se afişeze acestea utilizând pointeri. #include <iostream.h> void main(){ int a[20]; int *pa; int i,n; cout<<"Numărul de elemente= "; cin>>n; for(i=0;i<n;i++){ cout<<"Elementul"<<i<<"="; cin>>a[i]; } //afişarea vectorului folosind pointeri pa=&a[0]; for(i=0;i<n;i++){ cout<<*pa<<"\n"; pa++; } } Prima pate a programului nu con ine elemente noi, doar a doua parte meritând aten ie. Mai întâi ini ializam pointerul pa cu valoarea primului element al vectorului a. Ciclul for con ine următoarele prelucrări: • afişează valoarea aflata la adresa indicata de pointer; • aduna pointerul pa cu 1 Incrementarea pointerului pa are ca efect modificarea adresei memorate în pa. Noua adresa este adresa zonei de memorie corespunzătoare elementului următor, o adresa cu 2Byte mai mare decât precedenta. Observam ca mărirea pointerului cu o unitate înseamnă de fapt trecerea la următorul element din vector. Dacă vom introduce pentru n o valoare mai mare decât 20 (numărul maxim de elemente ale vectorului, aşa cum reiese din declara ie) atunci pointerul pa va depăşi zona de memorie alocata vectorului şi va referi o adresa la care se pot afla date importante pentru program. Urmările pot fi imprevizibile, de la blocarea programului până la blocarea sau închiderea calculatorului !!!

No iunea de func ie. Structura şi definirea func iilor
No iunea de func ie este o no iune de mare importanta în informatica, orice limbaj de programare furnizând facilită i de lucru cu func ii. În matematica, func ia era definita ca fiind tripletul (A, B, f), unde: A - domeniul de defini ie; B - codomeniul sau domeniul de valori; f - lege, conven ie, prin care fiecărui element din domeniul de defini ie i se asociază un unic element din codomeniu; În informatica, no iunea de func ie diferă pu in de modul matematic de abordare. În limbajul C, orice program trebuie să con ină obligatoriu o func ie numita main. Dacă prelucrările ce compun programul sunt foarte complexe, utilizarea unei singure func ii duce la un program greu de în eles şi depanat. În acest caz este bine ca problema de rezolvat să fie împăr ita în sub probleme prin a căror combinare să se ob ină rezolvarea problemei ini iale. Pentru rezolvarea fiecărei sub probleme se poate utiliza cate o func ie separata. Func ia main (“programul principal”) nu va mai fi de mare întindere, ea con inând doar apeluri către func iile deja definite. Decizia de a folosi o func ie poate fi luata şi în cazul în care anumite prelucrări trebuiesc realizate de mai multe ori, cum se va vedea şi în exemplul de mai jos. Exemplul 1. Să se calculeze aria a n triunghiuri, dacă se cunosc lungimile laturilor acestora.

26

#include <iostream.h> #include <conio.h> #include <math.h> float aria(int a, int b, int c){ float p,s; p=(float)(a+b+c)/2; s=sqrt(p*(p-a)*(p-b)*(p-c)); return s; } void main(){ int a,b,c; float S; int i,n; cout<<"Numărul de triunghiuri= "; cin>>n; i=1; for(i=1;i<=n;i++){ cout<<"a="; cin>>a; cout<<"b="; cin>>b; cout<<"c="; cin>>c; S=aria(a,b,c); cout<<"Aria triunghiului "<<i<< " este "<<S<<"\n"; } } Programul trebuie să calculeze aria a n triunghiuri, deci calculul ariei trebuie realizat de n ori. Am definit func ia aria, care calculează aria triunghiului cu lungimile laturilor a, b, c prin formula lui Heron. Structura func iei este următoarea: antet: float aria(int a, int b, int c) care con ine următoarele elemente: • tipul rezultatului func iei (codomeniul) - rezultatul func iei va fi o valoare de tipul declarat; • numele func iei: - numele nu poate fi identic cu un cuvânt cheie • lista parametrilor formali - variabilele de care depinde calcularea rezultatului (“argumentele func iei”); pentru fiecare parametru formal se specifica tipul. • corpul func iei:

{ float p,s; p=(float)(a+b+c)/2; s=sqrt(p*(p-a)*(p-b)*(p-c)); return s; } Corpul func iei con ine declara iile variabilelor utilizate în func ie şi prelucrările realizate de func ie. Variabilele utilizate în func ia aria, numite şi variabile locale, sunt semi perimetrul p şi aria s. Nu declaram variabilele a, b, c (parametrii formali nu se declara în corpul func iei) şi nici nu citim valori pentru ele în cadrul func iei. Orice func ie care are drept codomeniu un tip diferit de tipul void trebuie să con ină în corpul func iei o instruc iune return expresie; unde expresie este orice expresie corecta în C cu valoare de tipul declarat pentru codomeniu. Instruc iunea return este utilizata pentru a semnala faptul ca valoarea func iei este valoarea expresiei. În programul principal (func ia main) se citeşte numărul de triunghiuri n. Instruc iunea for va repeta de n ori (pentru fiecare triunghi) următoarele prelucrări: • se citesc valori pentru variabilele a, b, c care reprezintă lungimile laturilor triunghiului curent; • se calculează aria triunghiului pentru valorile date; • se afişează valoarea ariei triunghiului curent; Modul de utilizare al func iei aria este interesant. Atribuirea: S=aria(a,b,c); are următorul efect; • se evaluează expresia din dreapta atribuirii, expresie ce consta din apelul func iei aria pentru valorile a, ,b, c • evaluarea func iei înseamnă executarea prelucrărilor din func ie pentru valorile parametrilor formali a, b, c • valoarea întoarsa de func ie este valoarea variabilei s, care apare în return; • valoarea este memorata în zona de memorie a variabilei S. Func iile pot fi clasificate astfel: • func ii predefinite = func ii deja definite de autorii mediului de programare C şi grupate, în func ie de utilitatea lor, în biblioteci numite fişiere header. De exemplu, în biblioteca math.h sunt grupate func iile matematice, în string.h avem func iile de lucru cu şiruri de caractere, în iostream.h func ii pentru introducerea şi afişarea datelor, în malloc.h şi alloc.h func ii pentru alocarea memoriei… • func ii definite de utilizator = func ii scrise de creatorul unui program pentru acele prelucrări pentru care nu exista func ii predefinite (caz destul de frecvent); O func ie poate să aibă drept codomeniu orice tip predefinit scalar (întreg, caracter sau real), tip compus (uniune sau structura), precum şi pointeri la orice tip. Tipul void înseamnă ca func ia nu returnează nici o valoare. Programatorul poate să strângă func iile definite de el intr-un fişier header propriu, care se poate include în program prin 27

#include “nume_fisier.h”.

Apelul func iilor şi transferul parametrilor
Orice apari ie a numelui func iei înso ită de un număr de valori egal cu numărul parametrilor formali se numeşte apel al func iei. Puteam apela func ia şi în modul următor: cout<<aria(3,4,5); Aceasta instruc iune are ca efect afişarea ariei triunghiului cu laturile de lungimi 3, 4, 5. Observa i ca nu este obligatoriu ca parametrii din apel să fie variabile, ci pot fi expresii. Dacă func ia noastră ar apărea intr-o expresie ca 23*aria(3,4,5)2*aria(2,6,6), evaluarea expresiei ar începe cu executarea func iei aria pentru valorile 3, 4, 5 şi apoi pentru 2, 6, 6; după ce se ob in rezultatele celor doua apeluri ale func iei se realizează evaluarea celorlalte operatori (înmul irile şi apoi scăderea). Func ia în care apare un apel al altei func ii se numeşte func ie apelanta. Parametrii sunt de doua tipuri: parametri formali: valoare referin a (pointer) parametri efectivi (actuali) Parametrii sunt utiliza i pentru a putea transmite din func ia apelanta valorile necesare prelucrărilor efectuate de func ia apelata. Atunci când parametrii formali sunt utiliza i doar pentru a transmite valori în func ia apelata, aceşti parametrii se numesc parametri valoare. Dacă se doreşte transmiterea ca parametru a unei variabile, în scopul modificării valorii acesteia astfel încât modificarea să fie disponibila în func ia apelanta, avem de-a face cu un parametru formal referin a (se realizează printr-o variabila de tip pointer). Pentru a putea apela o func ie, trebuie să precizam, pe lângă numele acesteia, valorile parametrilor pentru care se realizează apelul (parametri actuali). Numărul parametrilor actuali dintr-un apel de func ie trebuie să fie acelaşi cu numărul parametrilor formali din defini ia func iei apelate şi să corespunda ca tip. De exemplu, în programul de mai sus, nu puteam apela func ia aria ca s = aria(4.56, 5, 5); deoarece primul parametru formal din defini ia func iei este definit ca de tip int, iar parametrul actual (4.56) este de tip double (incompatibilitate de tip). Nu este obligatoriu să existe parametrii formali intr-o func ie dacă nu este nevoie. De asemenea, nu toate func iile întorc o valoare. Aceste aspecte sunt tratate în următoarele exemple. Exemplul 2. Să se scrie o func ie care afişează pe ecran un număr dat de asteriscuri. void asterisc(int nr){ int j; for(j=1;j<=nr;j++) cout<<”* ”; } Func ia are un singur parametru formal, un întreg nr reprezentând numărul de asteriscuri ce trebuie afişate. Func ia poate fi apelata prin asterisc(10); care are ca efect afişarea a 10 asteriscuri.

Func ia nu întoarce nici o valoare, deoarece nu este necesar (n-are sens să întoarcem vreo valoare), deci codomeniul va fi tipul void. Din moment ce func ia nu întoarce vreo valoare, nu se va mai utiliza return. Exemplul 3. Scrie i o func ie care să afişeze un mesaj. void mesaj( ){ cout<<”Trăiască Reforma !!!”; } Func ia mesaj nu întoarce nici o valoare şi nici nu con ine parametrii formali (nu exista valori care să fie necesare execu iei func iei). Apelul func iei se va face prin: mesaj(); exact ca şi pentru func ia pre definită clrscr( ). Dacă am dori ca func ia să afişeze un mesaj dat, ob inem: void mesaj(char s[80]){ cout<<s; } Apelul func iei poate fi: mesaj("Trăiască pacea şi bunăstarea poporului chinez !!!”); Exemplul 4. Scrie i o func ie care stabileşte dacă un număr este prim. #include <iostream.h> int este_prim(int n){ int j; int este; este=1; //presupunem ca numărul este prim for(j=3;j*j<=n;j++) if(n%j==0) este=0; return este; } void main(){ int număr; cout<<”Introduceti numărul=”; cin>>număr; if(este_prim(număr)!=0) cout<<”Numărul este prim !”; else cout<<”Numărul nu este prim !”; } Exerci iul 1: Folosi i func ia este_prim pentru a afişa toate numerele prime mai mici decât un număr întreg dat. Exemplul 5. Scrie i o func ie care să citească un vector de numere întregi.

28

void citeste(int a[30], int n) { int k; for(k=0;k<n; k++) cin>>a[k]; } Func ia are doi parametri: variabila de tip vector care va retine numerele citite şi variabila n, care ne spune cate elemente trebuiesc citite. Func ia main poate să arate astfel: void main (){ int b[30]; int n; cout<<”n=”; cin>>n; citeste(a,n); } Exerci iul 6: Scrie i o func ie pentru afişarea elementelor unui vector şi adăuga i-o exemplului precedent. Exemplul 7. Scrie i o func ie care calculează suma elementelor dintr-un vector. int suma(int a[40], int n) { int k; int s; s=0; for(k=0;k<n;k++) s=s+a[k]; return s; } Este necesar să introducem ca parametru şi numărul de elemente din vector n. Exerci iul 8: Folosi i func ia de mai sus în programul de la exerci iul precedent. Exemplul 9. Scrie i o func ie care calculează ax, unde a real şi x întreg pozitiv. float putere(float a, int x) { int k; int p; //retine puterea p=1; for(k=1;k<=x;k++) p=p*a; return p; } Exerci iul 10. Modifica i func ia din exemplul 7 astfel încât să poată ridica pe a şi la puteri negative. Exemplul 11. Scrie i o func ie pentru a calcula cel mai mare divizor comun a 2 numere întregi folosind algoritmul lui Euclid.

#include <iostream.h> int cmmdc(int m, int n){ int r; do{ r=m%n; m=n; n=r; }while(r!=0); return m; } void main(){ int a,b; cout<<"Primul număr="; cin>>a; cout<<"Al doilea număr="; cin>>b; cout<<cmmdc(a,b); } Exemplul 12 Să se scrie o func ie care verifica dacă un întreg x apar ine unui vector v. int cauta(int v[50], int n){ int k; int apartine; apartine=0; //se presupune ca x nu este element în v for(k=0;k<n;k++) if(v[k]==x) apartine=1; return apartine; } Exemplul 13. Să se scrie o func ie care realizează suma a doua matrice cu m linii şi n coloane. void suma_mat(int a[20][20], int b[20][20], int c[20][20], int m, int n){ int j, k; //variabile contor în for for(j=0;j<m;j++) for(k=0;k<n;k++) c[j][k]=a[j][k]+b[j][k]; } Parametrii de care depinde rezolvarea sarcinii sunt: a - prima matrice b - a doua matrice c - matricea suma (rezultatul sumei) 29

m - numărul de linii n - numărul de coloane Exerci iul 5. Să se scrie func ia main corespunzătoare func iei de mai sus, precum şi func ii pentru citirea şi afişarea unei matrice. O situa ie interesanta apare atunci când vrem ca variabilele ce apar ca parametri formali să poată fi modificate în cadrul func iei, modificările fiind disponibile în func ia apelanta. Să luam următorul exemplu: Exemplul 14. Să se scrie o func ie care interschimbă valorile a doua variabile întregi. Varianta următoare este greşita: void schimba(int x, int y){ int temp; temp=x; x=y; y=temp; } void main(){ int a, b; cin>>a; cin>>b; schimba(a,b); cout<<a; cout<<b; } La prima vedere func ia pare corecta, insa lucrurile stau tocmai pe dos. Dacă introducem valorile 5 pentru a şi 7 pentru b, programul ar trebui să schimbe intre ele valorile celor doua variabile (a trebuie să devină 7, iar b să devină 5). Programul de mai sus nu va realiza acest lucru. Explica ia este ca, atunci când parametri formali sunt parametri valoare, chiar dacă se modifica valoarea lor în cadrul func iei aceste modificări nu vor avea efect în func ia apelanta (în cazul nostru, în func ia main nu va fi sesizabila schimbarea realizata în func ia schimba). Problema se poate rezolva dacă parametrii din func ia schimba vor fi parametri referin a (pointeri). Varianta corecta este următoarea: void schimba(int *x, int *y){ int temp; temp=*x; *x=*y; *y=temp; } Singura schimbare din func ia main este apelul func iei, care devine schimba(&a, &b); Explica ia corectitudinii acestei variante este aceea ca se lucrează cu adresele variabilelor, deci orice modificare a con inutului zonelor de memorie alocate variabilelor x şi y va fi resim ită şi în func ia main.

Parcurge i din nou exemplul 10 şi privi i cu aten ie antetul func iei suma_mat. Parametrul formal c este o matrice calculata ca suma matricelor a şi b, deci valoarea elementelor lui c este modificata în cadrul func iei, dar parametrul formal este corect? Răspunsul este afirmativ deoarece, după cum şti i, tablourile sunt tratate exact ca şi pointerii, deci tablourile care apar ca parametrii formali se considera parametri referin a.

30

Anexe
Recomandări pentru scrierea programelor
Conven ii generale de nume
Numele compuse reprezentând tipuri de date ar fi bine să con ină litere mari la începutul fiecărui cuvânt component. Vector, StudentBursier Numele compuse reprezentând variabile ar fi bine să înceapă cu literă mică şi să con ină litere mari la începutul fiecărui cuvânt component. contor, nrStud Astfel, variabilele se vor distinge uşor de tipuri şi se vor evita eventualele conflicte de nume, ca în declara ia: Line line. Numele de constante (inclusiv al celor folosite în enumerări) ar fi bine de scris cu litere mari în întregime, utilizându-se liniu a de subliniere pentru separarea cuvintelor componente. NR_MAX, SEMAFOR_ROSU, PI Numele compuse reprezentând metode, ca şi cele ale func iilor trebuie să descrie efectul respectivei metode sau func ii, recomandabil în format Camel Case (la început cu literă mică, apoi cu literă mare la începutul fiecărui cuvânt component) sumElem(), maxVect(), obtNume(), calcLungTotala() Numele compuse reprezentând spa ii de nume ar fi bine să con ină în întregime doar litere mici. analizor, managerdialog, fereastraprinc Numele pentru tipuri şablon ar fi bine să fie alcătuite dintr-o singură majusculă. template<class T>… template<class C, class D> ... Abrevierile şi acronimele nu trebuie scrise cu majuscule atunci când intră în componen a unui nume. exportHtmlSource();// NU: exportHTMLSource(); openDvdPlayer(); // NU: openDVDPlayer(); Ar fi bine ca variabilele globale s fie totdeauna referite utiliz nd operatorul "::". ::ferPrinc.open(), ::contextApl.obtNume() În general, e bine de evitat folosirea variabilelor globale, acestea putând fi înlocuite cu obiecte având un singur membru. Ar fi bine ca toate numele să fie scrise în română (în orice caz, într-o aceeaşi limbă). numeFisier; // NU: filNavn Engleza este limba general adoptată de comunitatea interna ională, însă este mai firesc ca programele destinate utilizatorilor români să con ină nume cât mai sugestive, în limba nativă. Ar fi bine ca variabilele cu domeniu mare de vizibilitate să aibă nume mai lungi, iar cele cu domeniu mic de vizibilitate să aibă nume scurte.

Este recomandat ca diversele variabile cu rol temporar sau de indici să aibă nume scurte, aceasta fiind o indiciu pentru programator că ele nu sunt folosite dincolo de limita a câtorva linii de cod. Numele cel mai des folosite pentru astfel de variabile întregi sunt: i, j, k, m, n, respectiv c sau d pentru cele de tip caracter. Numele unui obiect con ine de obicei numele clasei din care face parte, de aceea ar fi bine să se evite reluarea numelui clasei în numele date metodelor clasei. cerc.obtRaza(); // NU: cers.obtRazaCerc(); Inserarea numelui obiectului în numele metodelor pare naturală în cadrul defini iei clasei, dar se dovedeşte de prisos la utilizarea efectivă, cum se poate vedea şi din exemplul dat.

Conven ii specifice de nume
Termenii obtine/seteaza (obt/set, get/set) trebuie folosi i pentru metodele care accesează direct o dată membră a clasei. student.obtineNume(); matrice.obtElement(2, 4); angajat.seteazăNume(nume); matrice.setElement(2, 4, val); Practică generalizată în comunitatea programatorilor C++. In Java această conven ie tinde să se standardizeze. Ar fi bine ca denumirile listelor de obiecte să con ină sufixul List. stud (un student), studList (o listă de studen i) Lizibilitatea programului creşte de vreme ce numele unui obiect sugerează imediat cititorului tipul şi opera iile ce pot fi efectuate asupra lui. Ar fi bine de ataşat prefixul nr variabilelor reprezentând un număr de obiecte. nrPuncte, nrLinii Această nota ie este preluată din matematică. Ar fi bine ca variabilele iterative să fie numite i, j, k etc. Exemplu: for (int i = 0; i < nrLinii); i++) { ... } Această nota ie este preluată din matematică. Ar fi bine de ataşat prefixul is variabilelor şi func iilor booleene şi de evitat nega iile din cadrul numelor acestora isSet, isVisible, isFinished, isFound, isOpen NU: isNotSet, isNotVisible, isNotFound etc. Practică generalizată în comunitatea programatorilor C++ şi par ial impusă în Java. Utilizarea acestui prefix împiedică alegerea unor denumiri neinspirate de genul status sau flag pentru ac iunile booleene. Deoarece isStatus sau isFlag nu sună bine, programatorul va trebui să găsească denumiri mai sugestive. De asemenea, apar confuzii atunci când un astfel de nume este folosit împreună cu operatorul de nega ie logică, rezultând o dublă nega ie. Opera iile opuse trebuie să aibă denumiri antonime

â

ă

31

obt/set, add/remove, creaza/distruge, start/stop, insert/delete, increment/decrement, old/new, begin/end, first/last, up/down, min/max, next/previous, old/new, open/close, show/hide, suspend/resume, etc. Complexitatea opera iei de asociere a unui nume cu semantica sa este redusă datorită simetriei. Ar fi bine să fie evitate abrevierile în cadrul numelor. calculeazaMedia(); // NU: calcMed(); Trebuie avute în vedere două tipuri de cuvinte. Întâi, cuvintele obişnuite, care se găsesc în dic ionar. Acestea nu trebuiesc niciodată prescurtate. Nu scrie i niciodată: cmd în loc de comanda cp în loc de copie pt în loc de punct calc în loc de calculeaza init în loc de initializeaza etc. Apoi, expresiile din diferite domenii popularizate mai mult prin abrevieri/acronime ar fi bine de păstrat în aceste forme prescurtate ale lor. Nu scrie i niciodată: HypertextMarkupLanguage în loc de html CentralProcessingUnit în loc de cpu PriceEarningRatio în loc de pe etc. Constantele enumerative pot fi prefixate cu numele tipului lor comun. Exemplu: enum Color { COLOR_RED, COLOR_GREEN, COLOR_BLUE }; Astfel sunt furnizate informa ii suplimentare despre locul unde poate fi găsită declara ia, setul de constante înrudite şi conceptele reprezentate de constante.

class Student { public: float obtMedia () {return media} // NU! ... private: float media; } Astfel va fi uşor de regăsit fişierele asociate cu o clasă dată. Con inutul unui fişier nu trebuie să depăşească 80 de coloane. 80 de coloane este o dimensiune generalizată pentru editoarele de text, emulatoarele de terminal, imprimante şi debuggere, de aceea ar fi bine ca fişierele partajate de mai multe persoane să respecte această restric ie. Caracterele speciale de genul "TAB" sau "page break" trebuie evitate. Aceste caractere pot cauza probleme editoarelor de text, emulatoarelor de terminal, imprimantelor sau debuggerelor atunci când sunt folosite într-un mediu multi-programare sau multi-platformă. Trebuie bine eviden iat faptul că o instruc iune se întinde pe mai multe linii Atunci când o declara ie depăşeşte limita de 80 de coloane, ea este divizată pe mai multe linii. Este dificil de stabilit reguli rigide de divizare a liniilor, dar exemplele de mai sus dau o oarecare sugestie. În general: • Se poate trece pe o linie nouă după o virgulă • Se poate trece pe o linie nouă după un operator • Se aliniază noua linie cu începutul expresiei din linia precedentă

Includerea de fişiere şi includerea declara iilor
Fişierele antet trebuie să con ină o construc ie care să prevină incluziunile multiple. Conven ia este ca numele acestor construc ii să fie formate doar din litere mari şi să fie compuse din numele modulului de program, numele fişierului şi sufixul h. Exemple: #ifndef _LISTA_H #define _LISTA_H … #endif Scopul este evitarea erorilor de compilare. Această conven ie de nume este deja generalizată. Ar fi bine ca această construc ie să fie plasată chiar la începutul fişierului (înaintea tuturor declara iilor). Astfel, se va renun a la parsarea fişierelor iar timpul de compilare va fi redus. Ar fi bine ca instruc iunile de includere să fie sortate (crescător, după pozi ia lor ierarhică în sistem) şi grupate pe categorii (fiind lăsată o linie liberă între diversele grupe). Exemple:

Fişiere sursă
Ar fi bine ca fişierele antet C++ să aibă extensia .h. , iar fişierele sursă extensia .cpp. 1_1.cpp, 1_1.h Acestea toate sunt standarde C++ adoptate deja pentru extensiile de fişiere. Ar fi bine ca o declara ia unei clase să se găsească într-un fişier antet, iar defini ia ei - întrun fişier sursă, ambele cu un nume apropiat de cel al clasei. student.cpp, student.h Astfel va fi uşor de regăsit fişierele asociate cu o clasă dată. Ar fi bine ca toate defini iile să se găsească în fişiere sursă.

32

#include <fstream> #include <iomanip> #include <Xm/Xm.h> #include <Xm/DateImp.h> #include "ui/ProprietatiDialog.h" #include "ui/FereastraPrincipala.h" Este sugerat astfel şi ce module de program sunt implicate. Instruc iunile de includere trebuie să fie plasate doar la începutul fişierului. Practică generalizată. Sunt evitate efectele secundare ale compilării unor fişiere incluse datorită unor clauze #include ascunse printre liniile programului sursă.

Tipuri de date
Tipurile de date locale unui singur fişier pot fi declarate în interiorul acelui fişier. Implică ascunderea informa iilor. Sec iunile unei clase trebuie să fie, în ordine, public, protected şi private, fiecare trebuind eviden iată explicit. Celelalte eventuale sec iuni ar fi bine de lăsat deoparte. Criteriul de ordonare este "cel mai public întâi", astfel încât cei care doresc doar să utilizeze clasa ştiu că e suficient să citească defini iile până la întânirea sec iunilor protected şi private. Conversiile de tip trebuie făcute totdeauna explicit. Nu vă încrede i niciodată în conversiile implicite de tip. Exemple: medie = static_cast<float> nota // AŞA DA! valReala = valIntreaga; // AŞA NU! Prin aceasta programatorul indică folosirea conştientă a diferitelor tipuri şi a amestecului lor.

Variabile
Ar fi bine ca variabilele să fie ini ializate acolo unde sunt declarate. Aceasta dă siguran a că variabilele sunt valide în orice moment. Uneori este imposibil ca variabilele să fie ini ializate cu o valori valide chiar în momentul declarării lor: int x, y, z; obtCentru (&x, &y, &z); În astfel de cazuri este preferabilă varianta neini ializării decât ini ializarea cu valori nepotrivite. Variabilele nu trebuie să aibă niciodată un în eles dual. Asigurându-se o reprezentare unică pentru fiecare concept, creşte lizibilitatea programului şi se reduce riscul erorilor datorate efectelor secundare Ar fi bine ca utilizarea variabilelor globale să fie minimizată. În C++ nu există nici un motiv special pentru utilizarea acestor variabile. Acelaşi lucru este valabil pentru func iile globale şi pentru variabilele statice. Ar fi bine ca variabilele membre ale unei clase să nu fie declarate niciodată public.

Conceptele C++ de ascundere a informa iilor şi de încapsulare sunt violate prin folosirea variabilelor public. În locul lor ar putea fi folosite variabile private şi metode de accesare a acestora. Există o singură excep ie de la această regulă: în clasele reprezentând structuri de date neomogene (echivalentul unor struct din C) este mai indicată declararea variabilelor drept public. Re ine i faptul că tipul struct este men inut în C++ doar din motive de compatibilitate cu C, iar evitarea lui va spori lizibilitatea programului. În loc de acesta ar putea fi folosi class. Variabilele înrudite de acelaşi tip pot fi declarate împreună[3]. Ar fi bine ca pentru variabilele neînrudite să nu se folosească o aceeaşi declara ie. Exemplu: float x, y, z; int bursaIanuarie, bursaFebruarie, bursaMartie; Cerin a generală de a scrie declara iile pe linii separate nu se aplică situa iilor de genul celor de mai sus. Grupându-se variabilele astfel, creşte lizibilitatea. Ar fi bine ca simbolul ce indică o referin ă sau un pointer C++ să fie scris lângă numele variabilei şi nu lângă numele tipului de dată. Exemplu: float *x; // NU: float* x; int &y; // NU: int& y; Este discutabil dacă un pointer este o variabilă de un tip pointer (float* x) sau un pointer la un tip dat (float *x). Recomandarea de mai sus se bazează pe faptul că este imposibil să indici mai mult decât un pointer într-o declara ie de primul fel. De exemplu, float* x, y, z; este echivalent cu float *x; float y; float z;. Acelaşi lucru este valabil pentru referin e. Ar fi bine ca testele implicite de egalitate cu 0 să nu fie folosite decât pentru variabilele booleene şi pointeri. Exemplu: if (nrLinii != 0) // NU: if (nrLinii) if (valoare != 0.0) // NU: if (valoare) Valorile 0 (nule) pentru variabilele întregi şi reale nu sunt în mod necesar implementate de compilator prin valoarea binară 0. De asemenea, un test explicit sugerează imediat tipul testat. Pentru pointeri s-a generalizat practica testelor implicite de egalitate cu 0 (de ex. "if (line == 0)" în loc de "if (line)"), totuşi pot fi folosite şi testele explicite. Ar fi bine ca variabilele să fie declarate cu cel mai mic domeniu de vizibilitate posibil. Astfel sunt mai uşor de controlat ac iunile şi efectele secundare ale variabilelor.

Bucle repetitive
În construc ia for() trebuiesc incluse doar instruc iuni de control a buclei repetitive. Exemplu:

33

sum = 0; // NU: for (i = 0, sum = 0; i < 100; i++) for (i = 0; i < 100; i++) sum += value[i]; Programul devine mai uşor de între inut şi de citit, fiind astfel clar care variabile controlează bucla şi care instruc iuni o alcătuiesc. Ar fi bine ca variabilele iterative să fie ini ializate imediat înainte de bucla repetitivă. Exemplu: terminat = false; // NU: bool terminat = false; while (!terminat) { // ... } Aceste construc ii pot fi comparate cu instruc iunile goto şi de aceea este bine să fie utilizate doar atunci când oferă o mai mare claritate decât corespondentele din programarea structurată. Pentru buclele infinite ar fi bine de folosit forma while(true). Exemplu: while (true) { } for (;;) { // NU! } while (1) { // NU! } while (1) nu este prea sugestivă şi cu atât mai pu in for (;;), care pare a se referi la o buclă infinită.

Exemplu: isError = readFile (fileName); if (!isError) { … } else { … } Asigura i-vă că a i luat în calcul toate excep iile care ar putea împiedica execu ia normală a programului. Este important atât pentru claritate cât şi pentru performan ă. Ar fi bine ca expresia condi ională să se găsească pe o linie separată. Exemplu: if (isDone) // NU: if (isDone) doCleanup(); doCleanup(); Explica ia este legată de claritate şi de ra iuni de debug: este mai greu de evaluat o expresie care se află pe aceeaşi linie cu alte instruc iuni. Trebuie evitate instruc iunile executabile în interiorul expresiilor condi ionale. Exemplu: // Rău! if (!(fileHandle = open (fileName, "w"))) { … } // Mult mai bine! fileHandle = open (fileName, "w"); if (!fileHandle) { … } Mai ales pentru programatorii C++ începători astfel de expresii condi ionale ar fi foarte greoaie.

Expresii şi instruc iuni condi ionale
Expresiile condi ionale complexe trebuie evitate. În locul lor se pot folosi variabile booleene temporare. Exemplu: if ((nrElem < 0) || (nrElem > maxElem)|| nrElem == ultElem) { … } ar putea fi înlocuite prin: terminare = (nrElem < 0) || (nrElem > maxElem); repetare = (nrElem == ultElem); if (terminare || repetare) { … } Asignând variabile booleene (cu nume sugestive) expresiilor, programul devine mai explicit. Ar fi bine ca situa ia nominală (firească) să fie tratată pe ramura then iar excep ia pe ramura else a unei instruc iuni if [1].

Altele
Ar fi bine să fie evitate în program numerele "magice" (cu alte valori decât 0 sau 1), fiind indicat să se folosească în locul lor constante declarate la începutul programului. Dacă un număr nu are prin el însuşi o semnifica ie evidentă, e indicat să fie înlocuit printrun nume de constantă sau printr-o func ie care să-i acceseze valoarea. Trebuie totdeauna precizat explicit tipul returnat de o func ie. Exemplu: int getValue(){ // NU: getValue() … } Dacă nu este precizat explicit, C consideră drept int tipul returnat de o func ie. Acest lucru poate să nu fie cunoscut însă de cei care vor citi programul. Ar fi bine să nu fie utilizată instruc iunea goto. 34

Instruc iunile goto contravin ideii de cod structurat. Doar în foarte pu ine cazuri pot fi avute în vedere (de exemplu pentru ieşirea for ată dintr-un set foarte adânc de stucturi imbricate) şi doar dacă nu pot fi rescrise într-un cod structurat mai clar. Trebuie utilizat "0" în loc de "NULL". NULL face parte din biblioteca C standard, însă în C++ este deja demodat.

if (condi ie){ instruc iuni; }

Macheta unei instruc iuni
Ar fi bine ca dimensiunea unei indentări să fie de 2 spa ii. Exemplu: for (i = 0; i < nrElem; i++) a[i] = 0; Indentarea cu 1 caracter este prea mică pentru a eviden ia macheta logică a codului. O indentare cu mai mult de 4 caractere ar îngreuia citirea structurilor imbricate şi ar crea şansa ca o instruc iune să se întindă pe mai multe linii. Ar fi bine ca macheta unei instruc iuni să arate ca în exemplul 2 de mai jos (recomandat) sau ca în exemplul 1, însă în nici un caz nu trebuie să arate ca în exemplul 3. Exemple: while (!done) { doSomething(); done = moreToDo(); } while (!done) { done = moreToDodoSomething(); done = moreToDo(); } while (!done) { doSomething(); done = moreToDo(); } Exemplul 3 introduce un nivel suplimentar de indentare care nu eviden iază la fel de clar ca primele două exemple structura logică a codului. Emacs preferă varianta 3, VisualC varianta 2, iar începătorii o preferă pe prima (mai ales cei care au lucrat în Pascal). Ar fi bine ca o instruc iune if-else să fie scrisă sub următoarea formă: Exemple:

if (condi ie){ instruc iuni; } else{ instruc iuni; } Scrierea fiecărei păr i a instruc iunii if-else pe linii separate permite o mai uşoară citire şi manipulare a ei (de ex., renun area la clauza else). Ar fi bine ca o instruc iune for să fie scrisă sub următoarea formă: Exemplu: for (ini ializare; condi ie; actualizare){ instruc iuni; } La fel, decurge par ial din regula generală pentru structura unui bloc. Ar fi bine ca o instruc iune for cu bucla vidă să fie scrisă sub următoarea formă: Exemplu: for (ini ializare; condi ie; actualizare) { } Astfel este eviden iat faptul că instruc iunea for este construită în mod inten ionat cu bucla vidă. Totuşi, ar fi bine ca buclele vide să fie evitate. Ar fi bine ca o instruc iune while să fie scrisă sub următoarea formă: Exemplu: while (condi ie){ instruc iuni; } La fel, decurge par ial din regula generală pentru structura unui bloc. Ar fi bine ca o instruc iune do-while să fie scrisă sub următoarea formă: Exemplu: do{ instruc iuni; } while (condi ie); La fel, decurge par ial din regula generală pentru structura unui bloc. Ar fi bine ca o instruc iune switch să fie scrisă sub următoarea formă: Exemplu:

if (condi ie){ instruc iuni; } else if (condi ie){ instruc iuni; } else{ instruc iuni; }

35

switch (condi ie) { case ABC : instruc iuni; // Se execută şi următoarele instruc iuni până la primul break case DEF : instruc iuni; break; case XYZ : instruc iuni; break; default : instruc iuni; break; } Observa i faptul că fiecare cuvânt rezervat case este indentat relativ la cuvântul rezervat switch, ceea ce conduce la eviden ierea instruc iunii switch ca întreg. Observa i, de asemenea, spa iul dublu care precede caracterul ":". Dacă instruc iunea break lipseşte de pe o ramură case, ar fi bine să apară în locul ei un comentariu pentru a se şti că ignorarea ei a fost inten ionată. Ar fi bine ca o instruc iune try-catch să fie scrisă sub următoarea formă: Exemplu: try { instruc iuni; } catch (Exception excep ie) { instruc iuni; } La fel, decurge par ial din regula generală pentru structura unui bloc. Ramurile instruc iunilor if-else şi buclele intruc iunilor for sau while formate dintr-o singură instruc iune pot fi scrise fără paranteze acolade. Exemplu: if (condi ie) instruc iuni; while (condi ie) instruc iuni; for (ini ializare; condi ie; actualizare) instruc iuni; Există ş recomadarea generală (de ex., din partea firmei Sun) ca parantezele acolade să fie utilizate în toate cazurile. Totuşi, scopul parantezelor e acela de a grupa mai multe instruc iuni şi nu una singură.

Spa iile albe
Ar fi bine ca: operatorii conven ionali să fie încadra i de câte un caracter spa iu; după fiecare cuvânt rezervat să urmeze un spa iu; virgulele să fie urmate de un spa iu; operatorul : să fie încadrat de câte un caracter spa iu; după fiecare operator ; să urmeze un spa iu. Exemplu: a = (b + c) * d; // NU: a=(b+c)*d while (true) {// NU: while(true) ... doSomething (a, b, c, d); // NU: doSomething (a,b,c,d); case 100 : // NU: case 100: for (i = 0; i < 10; i++) { // NU: for (i=0;i<10;i++){ Astfel, fiecare componentă individuală a unei instruc iuni este pusă în eviden ă, iar lizibilitatea programului în ansamblu creşte. Ar fi bine ca în func iile care au parametri să fie plasat un caracter spa iu după "(" şi înainte de ")". Exemplu: doSomething( params ) // NU: doSomething(params); Astfel, fiecare nume în parte este eviden iat, crescând lizibilitatea programului. Dacă o func ie nu are parametri, spa iul poate fi omis (doSomething()) de vreme ce nu este posibilă nici o confuzie asupra numelui în acest caz. Ar fi bine ca unită ile logice din cadrul unui bloc să fie separate printr-o linie goală. Creşte astfel lizibilitatea programului. Ar fi bine ca, în cadrul declara iilor, variabilele să fie aliniate la stânga. Exemplu: AsciiFile *file; int nrPuncte; float x, y; Creşte astfel lizibilitatea programului. Numele variabilelor sunt astfel mai uşor de distins de numele tipurilor lor. Există şi opinia contrară că aceste constructii sunt "fragile" atunci când codul evoluează şi poate transforma programatorul într-o maşină de formatat. Recurge i la aliniere oriunde aceasta sporeşte lizibilitatea programului. Exemplu: 1. 2. 3. 4. 5.

36

If (a == lowValue) compueSomething(); else if (a == mediumValue) computeSomethingElse(); else if (a == highValue) computeSomethingElseYet();

while (true){ // Efectueaza ceva prelucrare(); } // NU: while (true) { // Efectueaza ceva prelucrare(); } Se evită astfel deteriorarea structurii logice a programului din cauza comentariilor.

value = (potential * oilDensity) / constant1 + (depth * waterDensity) / constant2 + (zCoordinateValue * gasDensity) / constant3;

minPosition = computeDistance (min, x, y, z); averagePosition = computeDistance (average, x, y, z);

switch (value) { case PHASE_OIL : strcpy (string, "Oil"); break; case PHASE_WATER : strcpy (string, "Water"); break; case PHASE_GAS : strcpy (string, "Gas"); break; } Există mai multe locuri într-un program unde pot fi plasate spa ii (în general în scopul alinierii) pentru a se spori lizibilitatea, chiar dacă astfel se contravine regulilor generale de scriere. Este dificil de formulat reguli privind alinierea codului, însă exemplele de mai sus ajută la formarea unei idei.

Bibliografie:
[1] Code Complete, Steve McConnel - Microsoft Press [2] Programming in C++, Rules and Recommendations, M Henricson, e. Nyquist, Ellemtel (Swedish telecom) [3] Wildfire C++ Programming Style, Keith Gabryelski, Wildfire Communications Inc. [4] C++ Coding Standard, Todd Hoff http://www.doc.ic.ac.uk/lab/cplus/c++.rules/ http://www.wildfire.com/~ag/Engineering/Development/C++Style/ http://www.possibility.com/Cpp/CppCodingStandard.html

Comentariile
Ar fi bine nu să comenta i codul care con ine capcane de programare, ci să-l rescrie i! În general, utilizarea comentariilor va fi minimizată la maxim dacă se scrie codul într-un mod foarte clar, utilizându-se nume sugestive şi o structură logică explicită. Ar fi bine ca toate comentariile să fie scrise în limba engleză. Engleza este limba preferată în orice mediu interna ional. Folosi i "//" pentru toate comentariile, inclusiv pentru cele care se întind pe mai multe linii. Exemplu: // Comentariu ce se întinde // pe mai multe linii. De vreme ce în limbajul C nu sunt permise comentariile având mai multe nivele, utilizarea simbolului // face posibilă ataşarea de comentarii tuturor sec iunilor programului. Simbolurile /* */ vor fi folosite în scopul depanării (eng. debug) (pentru a se verifica func ionarea unei sec iuni a programului, celelalte sec iuni pot fi marcate temporar drept comentarii) etc. Ar fi bine să existe un spa iu între "//" şi comentariul propriu-zis, care ar fi bine să înceapă cu literă mare şi să se termine cu punct. Ar fi bine să alinia i comentariile relativ la pozi ia codului la care ele se referă. Exemplu:

Anexe Elemente de algebră booleană
Generalită i
Transferul, prelucrarea şi păstrarea datelor numerice sau nenumerice în interiorul unui calculator se realizează prin intermediul circuitelor de comutare. Aceste circuite se caracterizează prin faptul că prezintă două stări stabile care se deosebesc calitativ între ele. Stările sunt puse în coresponden ă cu valorile binare “0” şi “1” sau cu valorile logice “adevărat” şi “fals” (din acest motiv se mai numesc şi circuite logice). Pornind de la aceste considerente, un domeniul al logicii matematice, (ştiin a care utilizează metode matematice în solu ionarea problemelor de logică) numit “algebra logicii” şi-a găsit o largă aplicare în analiza şi sinteza circuitelor logice. Algebra logicii operează cu propozi ii care pot fi adevărate sau false. Unei propozi ii adevărate i se atribuie valoarea “1”, iar unei propozi ii false i se atribuie valoarea “0”. O propozi ie nu poate fi simultan adevărată sau falsă, iar două propozi ii sunt echivalente din punct de vedere al algebrei logice, dacă simultan ele sunt adevărate sau false. Propozi iile pot fi simple sau compuse, cele compuse ob inându-se din cele simple prin legături logice de tipul conjunc iei ∧, disjunc iei ∨ sau nega iei ¬. 37

Bazele algebrei logice au fost puse de matematicianul englez George Boole (18151864) şi ca urmare ea se mai numeşte şi algebră booleană. Ea a fost concepută ca o metodă simbolică pentru tratarea func iilor logicii formale, dar a fost apoi dezvoltată şi aplicată şi în alte domenii ale matematicii. În 1938 Claude Shannon a folosit-o pentru prima dată în analiza circuitelor de comuta ie.

Definirea axiomatică a algebrei booleene
Algebra booleană este o algebră formată din: • elementele {0,1}; }
Obs. Acestea se mai pot numi Adevărat (engl. True) respectiv Fals (engl. False), Pornit (engl. On) respectiv Oprit (engl. Off)

5. Dacă mul imea M nu con ine decât două elemente, acestea trebuie să fie obligatoriu elementul nul 0 şi elementul unitate 1; atunci pentru ∀ x ∈ M există un element unic notat cu x cu proprietă ile: x ⋅ x = 0 principiul contradic iei x + x = 1 principiul ter ului exclus x este inversul elementului x. În definirea axiomatică a algebrei s-au folosit diferite nota ii. În tabelul următor se dau denumirile şi nota iile specifice folosite pentru diverse domenii: Matematică Prima lege de compozi ie x1 + x2 A doua lege de compozi ie x1 ⋅ x2 Elementul invers Logică Disjunc ie x1 ∨ x2 Conjunc ie x1 ∧ x2 Negare ¬x Tehnică SAU x1 + x2 SI x1 ⋅ x2 NU

• două opera ii binare numite SAU (OR engl.) notată simbolic + sau ∨ respectiv ŞI (AND engl.) notate simbolic prin ⋅ sau ∧; • opera ie unară numită NU nega ie, notată simbolic printr-o bară deasupra a ceea ce se neagă sau cu simbolul ¬. Opera iile se definesc astfel: Şi Sau Nu Sau exclusiv ⊕ 0+0=0 0⋅0=0 ¬0 = 1 0 ⊕ 0 = 0 0+1=0 0⋅1=0 ¬1 = 0 0 ⊕ 1 = 1 1+0=0 1⋅0=0 1⊕ 0 =1 1+1=1 1⋅1=1 1⊕1 = 0 Axiomele algebrei booleene sunt următoarele: Fie o mul ime M compusă din elementele x1, x2,…xn, împreună cu opera iile ⋅ şi +. Această mul ime formează o algebră dacă: 1. Mul imea M con ine cel pu in 2 elemente distincte x1 ≠ x2 (x1,x2∈ M); 2. Pentru ∀ x1 ∈ M, x2 ∈ M avem: x1 + x2 ∈ M şi x1 ⋅ x2 ∈ M 3. Opera iile ⋅ şi + au următoarele proprietă i: a. sunt comutative x1 ⋅ x2 = x2 ⋅ x1 x1 + x2 = x2 + x1 b. sunt asociative x1 ⋅ (x2 ⋅ x3) = (x1 ⋅ x2) ⋅ x3 x1 + (x2 + x3) = (x1 + x2) + x3 c. sunt distributive una fa ă de cealaltă x1 ⋅ (x2 + x3) = x1 ⋅ x2 + x1 ⋅ x3 x1 + (x2 ⋅ x3) = (x1 + x2) ⋅ (x1 + x3) 4. Ambele opera ii admit câte un element neutru cu proprietatea: x1 + 0 = 0 + x1 = x1 x1 ⋅ 1 = 1 ⋅ x1 = x1 unde 0 este elementul nul al mul imii, iar 1 este elementul unitate al mul imii.

x

x

Proprietă ile algebrei booleene
Plecând de la axiome se deduc o serie de proprietă i care vor forma reguli de calcul în cadrul algebrei booleene. Aceste proprietă i sunt: 6. Principiul dublei nega ii x = x dubla nega ie duce la o afirma ie 7. Idempoten a x⋅x=x x+x=x 8. Absorb ia x1 ⋅ (x1 + x2) = x1 x1 + (x1⋅ x2) = x1 9. Proprietă ile elementelor neutre x⋅0=0 x⋅1=x x+0=x x+1=1 10. Formulele lui De Morgan

x1 ⋅ x 2 = x1 + x 2

x1 + x 2 = x1 ⋅ x 2
Aceste formule sunt foarte utile datorită posibilită ii de a transforma produsul logic în sumă logică şi invers. Formulele pot fi generalizate la un număr arbitrar de termeni: 38

x1 ⋅ x 2 ⋅ ... ⋅ x n = x1 + x 2 + ... + x n x1 + x 2 + ... + x n = x1 ⋅ x 2 ⋅ ... ⋅ x n
11. Principiul dualită ii – dacă în axiomele şi proprietă ile algebrei booleene se interschimbă 0 cu 1 şi + cu ⋅, sistemul de axiome rămâne acelaşi, în afara unor permutări. Verificarea proprietă ilor se poate face cu ajutorul tabelelor de adevăr şi cu observa ia că două func ii sunt egale dacă iau aceleaşi valori în toate punctele domeniului de defini ie. Prin tabelul de adevăr se stabileşte o coresponden ă între valorile de adevăr ale variabilelor şi valoarea de adevăr a func iei. Obs. Comutativitatea şi asociativitatea pot fi extinse la un număr arbitrar, dar finit, de termeni, indiferent de ordinea lor.

1 1

1 1

0 1

0 0

1 0

0 0

0 0

0 0

1 1

0 1

1 0

1 1

Baze de numera ie
Omul, din cele mai vechi timpuri încerca să realizeze măsurători sau compara ii. Deoarece în elesurile "mai înalt ca" sau" mai mare ca" nu erau suficiente a trebuit să introducă numere pentru a putea exprima aceste lucruri. Era uşor de spus că un obiect era de două ori mai mic ca altul sau de trei ori mai valoros. Aceste compara ii sau măsurători se făceau uneori între obiecte destul de diferite între ele şi erau specifice trocului. (ex. î i dau 8 bucă i mari de sare pentru un miel etc.). În vechime oamenii socoteau pe degetele de la mâini, astfel că având zece degete erau capabili să realizeze opera ii cu valori până la zece. Evident că, pentru a nu ine minte prea multe lucruri trebuiau să-şi noteze calculate realizate. Există mai multe solu ii care rezolvau problema. Cea mai simplă spunea că trebuie scris câte un semn pentru fiecare unitate (de ex. pentru 3 obiecte erau desenate fie | | |, fie ooo sau orice alt semn). Pentru a putea reprezenta valori mai mari, trebuiau pur si simplu scrise prea multe astfel de simboluri. Cu cat valoarea de reprezentat era mai mare rea nevoie de o repetare a mai multor simboluri de acelasi fel (vă imaginati cât de fericiti a i fi sa desena i o suta de linii pentru a repezenta valoarea 100). Pentru a simplifica reprezentarea trebuiau născocite alte simboluri care să reprezinte valorile mai mari. Astfel, romanii foloseau diferite simboluri pentru a indica diferite "ponderi" ale valorilor (ex. V pentru 5, X pentru 10, L pentru 50...). Această metodă s-a dovedit a fi greoaie deoarece opera iile simple erau destul de greu de realizat (de exemplu aduna i MCLIV cu CMIX). O altă idee era de a folosi câte un simbol diferit pentru fiecare valoare de reprezentat (este ca si cum ai identifica grafic fiecare deget de la mâna), scrierea arabă (care de fapt este la origine chinezească şi care este cea folosită în zilele noastre) este un astfel de exemplu. Astfel avem simbolurile 0, 1, 2, 3,…, 9 (deci în total 10) care fiecare are o valoare diferită (de la 0 care nu are valoare până la 9 practic fiecare deget de la mână avea un simbol primul deget 1 al doilea 2 etc. iar ultimul al zelelea 0). Scrierea pe acre o folosim noi (arabă) este una pozitională adică de exemlu numerele mai mari ca 10 erau scrise pe pozi ia a doua sau mai mare în număr (astfel că de fapt 23 nu însemna 2 obiecte şi încă 3 ci de două ori 10 obiecte şi încă 3). Acest stil de a scrie valori este folosit şi în zilele noastre şi este cunoscut de către toată lumea. Un număr de mai multe cifre scris în baza 10 de forma:

Tabele de adevăr
Unei func ii booleene cu n variabile îi putem asocia un tabel care are 2n + 1linii care corespund celor 2n combina ii posibile ale variabilelor, precum a n+1 coloane care reprezintă cele n variabile plus una care reprezintă valorile corespunzătoare ale func iei. De exemplu pentru func ia F(x,y)=x+y avem 2 variabile deci vom vrea un tabel cu 5 linii si 3 coloane x y F(x,y)=x+y 0 0 0 0 1 1 1 0 1 1 1 1 In cazul care func ia booleană este exprimată sub forma de expresie, pentru a ajunge la tabelul de adevăr trebuie realizate mai multe calcule prin care să ne dăm seama care este tabelul de adevăr corespunzător func iei date. De exemplu func ia: f ( x, y , z ) = x ⊕ y + x y + x ⊕ z pentru a ajunge la tabelul de adevăr trebuie să realizăm mai multe calcule într-un mod asemănător unor calcule algebrice obişnuite numai că folosind regulile si principiile algebrei booleene. Pentru că în formularea func iei apare negatul pentru z si pentru z în primul rând vom calcula acele valori apoi succesiv vom construi tabelul de adevăr pentru func ia dată. Pentru aceasta

x n x( n +1) ...x0
poate fi văzul ca o sumă de produse de forma
x a 0 0 0 0 1 1 y b 0 0 1 1 0 0 z c 0 1 0 1 0 1
y

(1) (2)

z

x⊕y

xy

x⊕ y + xy

x ⊕ y + xy

x⊕z
j = a ⊕e

x⊕z
k = j

F
l =i+k

d 1 1 0 0 1 1

e 1 0 1 0 1 0

f = a ⊕b g = a ⋅ d

h= f +g

i=h

x n * 10 n + x n +1 * 10 n −1 + ... + x0 * 10 0

0 0 1 1 1 1

0 0 0 0 1 1

0 0 1 1 1 1

1 1 0 0 0 0

1 0 1 0 0 1

0 1 0 1 1 0

1 1 0 1 1 0

Se observă că, în expresia de mai sus valoarea 10 este constantă şi de fapt reprezintă baza de numera ie folosită. Baza de numera ie este o valoare care indică numărul de simboluri folosite pentru a reprezenta un număr. Un număr care este scris în baza de numera ie b 39

y n y ( n +1) ... y 0 (b )
are forma de mai jos.

(3)

Transformarea numerelor întregi din baza 10 în altă bază de numera ie
Pentru această ac iune, inem cont de faptul că un număr scris în baza 10 de fapt reprezintă o cantitate egală cu valoarea scrisă. El trebuie regrupat pentru a putea fi reprezentat în altă bază de numera ie. Opera ia se realizează prin împăr iri succesive ale câturilor rezultate la noua bază de numera ie. Procedând astfel vom ob ine rezultatul scontat, citind invers toate resturile ob inute Exemplu1: 292(10)=?(6) Rezultatul final este 1204(6) 292 6 24= 48 6 =52 48 8 6 =48 =0 6 1 6 ==4 2 0 0 1 Exemplul2: 2575(10)=?(16)
2575 16== =97= =96= ==15 ===0 ==15(F) 16 160 16 ==0 16 10 =0 10(A) 16 0

y n * b n + y n+1 * b n −1 + ... + y 0 * b 0 (4)
O bază de numera ie <10 va folosi mai pu ine simboluri (de exemplu baza de numera ie 7 va folosi doar 7 simboluri 0, 1, 2, 3, 4, 5, şi 6), iar >10 va avea mai multe (de exemplu baza 15 are nevoie de 15 simboluri). Pentru că nu există decât 10 simboluri pentru a reprezenta cifre şi nu putem folosi decât un singur simbol pentru reprezentare (deoarece în caz contrar sar crea confuzie) pentru fiecare cifră în parte, deşi putem folosi orice fel de simbol pentru a reprezenta cifre a căror valoare este mai mare ca 10, totuşi vom utiliza litere (majuscule sau minuscule de fapt nu contează) pentru a reprezenta simbolurile lipsă. Astfel pentru baza 15 vom avea 15 simboluri şi anume 0, 1, 2, 3, 4, 5, 6,7, 7, 8, 9, A, B, C, D, E (A va înlocui un simbol care va avea valoarea 10 şi va fi al unsprezecelea simbol de reprezentare, B-11, C-12, etc.). Exemple de numere corecte sunt 123(5), 7A3(11) etc. Exemple de numere scrise greşit sunt 264(5), 73X2(16), deoarece simbolurile 6 respectiv X nu există în bazele de numera ie 5 respectiv 16.

Transformarea numerelor întregi din altă bază de numera ie în baza 10
Pentru această ac iune, plecăm de la prezentarea unui număr în baza de numera ie b conform (4). Pentru a transforma un număr întreg din orice bază de numera ie în baza zece trebuie să desfacem grupele prin care a fost scris numărul respectiv. Astfel nu avem nimic de făcut altceva decât să calculăm în baza 10 valoarea finală folosind pentru aceasta formula (4) Exemplu: 745(8)=7*82+4*81+5*80 adică =7*64+4*8+5=448+32+5=485(10) Un caz mai complicat este dacă baza de numera ie este mai mare ca zece şi cuprinde numere a căror valoare este mai mare ca zece Exemplu: 3CB(15)=3*152+C*151+B*150=3*225+12*15+ 11*1=675+180+11=866(10) 60B(16)=6*162+0*161+B*160=1536+0+11 =1547(10) Pentru aceste trasformări este important sa va reamintiti faptul cifrele din număr sunt pozi ionale şi nu trebuie transformate individual în baza 10 de exemplu este gresit să realizăm transformarea astfel: 60B(16)=6011(16)=6*163+0*162+1*161+1*160=…

Observând că 10 corespunde simbolului A şi 15 simbolului F rezultatul final este A0F(16) Pentru aceste trasformări este important sa va reamintiti faptul cifrele din număr sunt pozi ionale şi deci resturile împăr irii trebuie transformate individual în noua bază de numera ie de exemplu este gresit să realizăm transformarea de mai sus 2575(10)=?(16) astfel: 2575(10)=10015(16)

Cazuri speciale de transformări
Se ştie că, toate calculatoarele func ionează în baza de numera ie 2, adică simbolurile folosite de calculator sunt doar „0” şi „1”. Acest lucru deranjează destul de mult, deoarece oamenii sunt obişnui i cu baza de numera ie 10 şi le este foarte greu să folosească noua bază de numera ie. Pentru ca programarea să fie mai uşoară trebuia găsită o cale de compromis. Această cale ar fi utilizarea bazelor de numera ie 8 sau 16 care sunt relativ apropiate de baza 10 şi pe de altă parte permit o schimbare uşoară de bază. Observăm că 8=23 sau că 16=24. Acest lucru este foarte important pentru că puterile lui doi au o anumită semnifica ie (arătată mai jos).

40

Se ştie ca folosind o cifră, în baza b putem ob ine b valori diferite (adică două pt. baza 2, cincisprezece pentru baza 15 etc.). Folosind două cifre vom ob ine b2 valori deoarece vom ob ine pentru fiecare valoare posibila pentru a doua cifră b valori prin modificarea primei cifre adică b*b sau b2. De exemplu pentru baza 3 vom ob ine 9 valori, pentru baza 2 patru valori etc. Generalizând, pentru o valoare de n cifre scrisă în baza b vom ob ine bn valori. Comparând baza 8 cu baza 2 vom observa ca un număr de 3 cifre din baza doi are tocmai opt valori posibile (nici mai multe nici mai pu ine). La fel baza 16 se echivalează cu un număr de patru cifre în baza 2. Ne existând alte valori, trecerea din baza 2 în baza 8 sau 16 se face doar prin grupări de cifre. Pentru uşurin ă în calcul putem folosi două tabele, cu ajutorul cărora putem realiza direct echivalarea. Baza 2 Baza 8 Baza 2 Baza 16 000 0 0000 0 001 1 0001 1 010 2 0010 2 011 3 0011 3 100 4 0100 4 101 5 0101 5 110 6 0110 6 111 7 0111 7 1000 8 1001 9 1010 A 1011 B 1100 C 1101 D 1110 E 1111 F Exemple: 1100111000111011011101110010101(2)=?(8) pentru a realiza transformarea din baza 2 în baza 8 vom grupa tot cate trei cifre binare începând de la dreapta spre stânga, iar dacă pentru ultima grupă nu sunt destule cifre atunci vom completa grupul cu zerouri. 001 100 111 000 111 011 011 101 110 010 101 1 4 7 0 7 3 3 5 6 2 5 =14707335625(8)
110.0111.0001.1101.1011.1010.0011.1001.0101(2)=?(16)

Tabela de Coduri ASCII
Caracterele SCII sunt împăr ite în: • caractere grafice (adică acele caractere care pot fi reprezentate printr-un simbol grafic) intervalul [33,126] • precum şi caractere negrafice care cuprind valorile {[0,32], 127} Coduri ASCII în zecimal 000 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 NUL SOH STX ETX EOT ENQ ACK BEL BS HT NL VT NP CR SO SI 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 SP ! " # $ % & ' ( ) * + , - . / 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 0 1 2 3 4 5 6 7 8 9 : ; < = > ? 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 @ A B C D E F G H I J K L M N O 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 P Q R S T U V W X Y Z [ \ ] ^ _ 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 ` a b c d e f g h i j k l m n o 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 p q r s t u v w x y z { | } ~ DEL Coduri ASCII în hexazecimal 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F NUL SOH STX ETX EOT ENQ ACK BEL BS HT NL VT NP CR SO SI 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F SP ! " # $ % & ' ( ) * + , - . / 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 0 1 2 3 4 5 6 7 8 9 : ; < = > ? 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F @ A B C D E F G H I J K L M N O 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F P Q R S T U V W X Y Z [ \ ] ^ _ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F ` a b c d e f g h i j k l m n o 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F p q r s t u v w x y z { | } ~ DEL Coduri ASCII în octal 000 001 002 003 004 005 006 007 010 011 012 013 014 015 016 017 NUL SOH STX ETX EOT ENQ ACK BEL BS HT NL VT NP CR SO SI 020 021 022 023 024 025 026 027 030 031 032 033 034 035 036 037 41

pentru a realiza transformarea din baza 2 în baza 16 vom grupa tot cate patru cifre binare începând de la dreapta spre stânga, completând de asemenea ultimul grup cu zerouri. 0110 0111 0001 1101 1011 1010 0011 1001 0101 6 7 1 D B A 3 9 5 =671DBA395(16) Observa ie: Folosind combinat metoda descrisă mai sus este foarte uşor de convertit un număr din baza 8 în baza 16 sau invers folosind baza 2 ca intermediar.

DLE DC1 DC2DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US 040 041 042 043 044 045 046 047 050 051 052 053 054 055 056 057 SP ! " # $ % & ' ( ) * + , - . / 060 061 062 063 064 065 066 067 070 071 072 073 074 075 076 077 0 1 2 3 4 5 6 7 8 9 : ; < = > ? 100 101 102 103 104 105 106 107 110 111 112 113 114 115 116 117 @ A B C D E F G H I J K L M N O 120 121 122 123 124 125 126 127 130 131 132 133 134 135 136 137 P Q R S T U V W X Y Z [ \ ] ^ _ 140 141 142 143 144 145 146 147 150 151 152 153 154 155 156 157 ` a b c d e f g h i j k l m n o 160 161 162 163 164 165 166 167 170 171 172 173 174 175 176 177 p q r s t u v w x y z { | } ~ DEL

SYN ETB CAN EM SUB ESC FS GS RS US SP

22 23 24 25 26 27 28 29 30 31 32

16 17 18 19 1a 1b 1c 1d 1e 1f 20

26 27 30 31 32 33 34 35 36 37 40

Synchronous Idle End Of Transmission Block – Sfârşit Transmisie Bloc Cancel – Anulare End Of Medium – Sfârşit De Mediu Substitute - Înlocuire Escape File Separator – Separator de Fişier Group Separator – Separator de Grup Record Separator – Separator de Înregistrare Unit Separator – Separator de Unitate Space - Spa iu

Cod NUL SOH STX ETX EOT ENQ ACK BEL BS HT NL VT NP CR SO SI DLE DC1 DC2 DC3 DC4 NAK

Numele simbolului ASCII Baza de numera ie Explica ie 10 16 8 0 0 0 Null – Terminare Şir C 1 1 1 Start Of Heading – Început De Antet 2 2 2 Start Of Text– Început De Text 3 3 3 End Of Text – Sfârşit De Text 4 4 4 End Of Transmission – Sfârşit De Transmisie 5 5 5 Enquiry – Interogare 6 6 6 Acknowledge – Acceptare 7 7 7 Bell – Sunet 8 8 10 Backspace – Spa iu Înapoi 9 9 11 Horiyontal Tab – Tabulator Orizontal 10 a 12 New Line (Or LF, Line Feed) – Linie Nouă 11 b 13 Vertical Tab – Tabulaor Vertical 12 c 14 New Page (Or FF, Form Feed) – Pagină Nouă 13 d 15 Carriage Return – Retur De Car 14 e 16 Shift Out 15 f 17 Shift In 16 10 20 Data Link Escape 17 11 21 Device Control 1 – Control Dispozitiv 1 18 12 22 Device Control 2 – Control Dispozitiv 2 19 13 23 Device Control 3 – Control Dispozitiv 3 20 14 24 Device Control 4 – Control Dispozitiv 4 21 15 25 Negative Acknowledge – Acceptare Negativă 42

Sign up to vote on this title
UsefulNot useful