39183180 Introducere in Ansi c v2

214
Introducere in ANSI C++ Introducere Initial, aceasta lucrare a fost conceputa avand ca instrument de lucru mediul de programare Borland C++ 3.1, varianta de DOS. Aparitia standardelor ANSI pentru C si C++ si in primul rand introducerea suportului pentru acest standard in majoritatea compilatoarelor (gcc, g++, bcc32 etc.), a dus la faptul ca unele dintre sursele existente sa nu mai fie viabile. Capitolele au fost modificate in asa fel incat un utilizator familiarizat cu mediul BC++ 3.1 sa poate face intr-un mod accesibil trecerea de la acest mediu spre variantele free oferite pe piata cu suport ANSI C++. Lucrarea a fost editata in intregime sub programul OpenOffice Writer, oferit in (probabil) toate distributiile gratuite de Linux. In acest caz, sistemul pe care s-a lucrat este SUSE 9.3. Am folosit pentru editarea programelor sursa utilitarul Kate, avand optiunile de editare specifice C++ activate, pentru compilare compilatorul g++, iar pentru depanare Kdbg, o interfata pentru gdb, depanatorul GNU. Urmatoarele volume vor contine: Prezentarea bilbiotecii standard ANSI C++ Programare orientate obiect in ANSI C++ STD (Standard Template Library) - 1 -

Transcript of 39183180 Introducere in Ansi c v2

Introducere in ANSI C++

Introducere

Initial, aceasta lucrare a fost conceputa avand ca instrument de lucru mediul de programare Borland C++ 3.1, varianta de DOS. Aparitia standardelor ANSI pentru C si C++ si in primul rand introducerea suportului pentru acest standard in majoritatea compilatoarelor (gcc, g++, bcc32 etc.), a dus la faptul ca unele dintre sursele existente sa nu mai fie viabile.

Capitolele au fost modificate in asa fel incat un utilizator familiarizat cu mediul BC++ 3.1 sa poate face intr-un mod accesibil trecerea de la acest mediu spre variantele free oferite pe piata cu suport ANSI C++.

Lucrarea a fost editata in intregime sub programul OpenOffice Writer, oferit in (probabil) toate distributiile gratuite de Linux. In acest caz, sistemul pe care s-a lucrat este SUSE 9.3.

Am folosit pentru editarea programelor sursa utilitarul Kate, avand optiunile de editare specifice C++ activate, pentru compilare compilatorul g++, iar pentru depanare Kdbg, o interfata pentru gdb, depanatorul GNU.

Urmatoarele volume vor contine:

– Prezentarea bilbiotecii standard ANSI C++

– Programare orientate obiect in ANSI C++

– STD (Standard Template Library)

- 1 -

Introducere in ANSI C++

Capitolul 1 – Primul program

1.1 Structura generala a unui program C++

In general, structura surselor C++ ce apar in lucrare este urmatoarea:

Sursele C++ reprezinta fisiere text care contin codul pe care dorim sa il rulam pe calculator. Scrierea acestora se poate face in orice editor de text care permite salvarea intr-un format text „curat”, fara detalii de formatare cum ar fi cele introduse de catre un procesor de text (MS Word, OpenOffice Write etc.). Textul sursa va fi salvat sub un nume sugestiv si cu extensia cpp.

Pentru a putea fi rulat, textul sursa trebuie sa fie „tradus” intr-o forma executabila de catre calculator. Fara a intra in amanunte, sursa trebuie compilata, apoi link-editata, obtinand un program executabil. In Figura 1.1 dam o schema simplificata a acestui proces.

- 2 -

// Comentariu (enuntul problemei)

# directive_preprocesor

prototipuri_functii;declaratii_variabile globale;

int main(){declaratii_variabile_locale;instructiuni;

}

definire_functii

Introducere in ANSI C++

Figura 1.1 – Trecerea de la textul sursa la fisierul executabil

1.2 Hi man!

Ca prim program vom implementa urmatoarea problema:

P1. Realizati un program C++ care afiseaza mesajul „Hi man!” pe ecran.

Vom rezolva problama in trei variante, una specifica C, a doua folosind mediul Borland C++ 3.1 si a treia varianta respectand standardul ANSI C++.

Limbajele C si C++ nu dispun de catre instructiuni de intrare/iesire. Pentru a putea citi valori de la tastatura sau pentru afisa pe ecran vom folosi biblioteci care „imbogatesc” limbajul cu diferite functii utile. In acest caz, linia 1 include biblioteca standard stdio.h (standard input/output).

Orice program C++ contine o functie numita main, functie cu care incepe

executia programului. Linia 2 contine antetul acestei functii. Sfarsitul liniei 2 contine '{', care reprezinta inceputul unui bloc, terminat in linia 4.

Intre acolade sunt scrise instructiunile programului, in acest caz linia 3, linie pe care apelam functia printf, functie care realizeaza afisarea mesajului dorit pe ecran.

- 3 -

Linkeditare

nume.cpp nume.exe

// P1_1. Realizati un program C++ care afiseaza mesajul „Hi man!” pe ecran.

#include <stdio.h> // linia 1

int main(){ // linia 2

printf("Hi man!"); // linia 3

} // linia 4

Compilare

Introducere in ANSI C++

In varianta urmatoare vom folosi biblioteca iostream, introdusa in C++ si care implementeaza asa numitele fluxuri de intrare/iesire.

Diferentele fata de primul exemplu le constatam in linia 1, unde am inclus o alta biblioteca ce ne ofera acees la operatiile de intrare iesire, respectiv in linia 3, unde, pentru afisare folosim o altfel de constructie.

Cei obisnuiti cu compilatoarele mai vechi, cum ar fi bcc.exe, compilatoriul care vine cu mediul Borland C++ 3.1, vor ramane surprinsi ca sursa de mai sus nu functioneaza (iar daca main ar fi returnat void, am fi avut doua erori!).

Varianta care respecta standardul ANSI C++ este urmatoarea:

Observam lipsa extensiei „.h” din linia 1, respectiv specificarea spatiului de nume std in linia 3.

1.3 Compilarea si rularea programului

1.3.1 In mediul Borland C++ 3.1

In cazul in care avem pornit mediul, putem rula programul prin apasarea combinatiei Ctrl+F9. Mediul va a apela automat compilatorul, apoi linkeditorul, va genera fisierul executabil, apoi il va rula. Pentru vizualizarea efectiva a rezultatului, apasam Alt+F5 pentru vizualizarea iesirii.

- 4 -

#include <iostream.h> // linia 1

int main(){ // linia 2 cout << "Hi man!"; // linia 3} // linia 4

#include <iostream> // linia 1

int main(){ // linia 2 std::cout << "Hi man!"; // linia 3 } // linia 4

Introducere in ANSI C++

In cazul in care am editat sursa intr-un alt editor, de exemplu notepad, pentru a rula aplicatia dorita vom realiza manual trecerea spre fisierul executabil, in felul urmator:

pas 1. Copiem sursa in directorul c:/borlandc/bin (pas facultativ, dar in acest mod simplificam explicatiile)

pas 2. .. > bcc p01.cpp (in cazul in care sursa a fost corecta, obtinem la acest pas fisierul p01.obj)

pas 3. .. > tlink p01.obj (obtinem p01.exe)

pas 4. .. > p01.exe (este rulat programul)

1.3.2 Folosind compilatorul g++

Rulam comanda g++ p01.cpp, din directorul in care avem textul sursa, si obtinem fisierul executabil a.out, care poate fi lansat in executie prin comanda ./a.out .

Daca dorim specificarea unui alt nume de fisier executibail, comanda de compilare va contine optiunea -o. De exemplu g++ -o Hi_Man p01.cpp va genera executabilul Hi_Man, iar rularea se va face prin ./Hi_Man .

- 5 -

Introducere in ANSI C++

Capitolul 2 – Notiuni de baza

2.1 Notiunea de algoritm

Numim algoritm o succesiune de pasi care rezolva o clasa de probleme. Vorbim de clase de probleme in urmatorul sens: daca 2x+3=0, respectiv x-4=0 reprezinta probleme, ax+b=0 va reprezenta clasa de probleme ce formeaza ecuatia de gradul I.

Un algoritm este caracterizat prin sintagma „noi muncim, nu gandim!”, in sensul ca executia pasilor algoritmului are loc fara nici un efort creator.

In general, in cadrul unui algoritm avem trei sectiuni distincte: date e intrare, rezolvarea efectiva a problemei si date de iesire, conform Figurii 2.1.

Figura 2.1 – Forma generala a unui algoritm

Pentru a evidentia pasii de urmat vom lua ca exemplu urmatoarea problema.

P1. Realizati un algoritm care calculeaza suma a doua valori.

Inainte de a incepe rezolvarea problemei trebuie sa ne fie clar care sunt datele initiale de la care se pleca, respectiv ce anume se cere. In acest caz, datele de intrare sunt reprezentate de catre a si b, iar data de iesire va fi S, care respecta datele problemei (adica S=a+b).

- 6 -

Date de intrare Date de iesireRezolvarea

problemei

Introducere in ANSI C++

Algortimul este:

Date de intrare (a,b)

citeste a si b

Rezolvarea problemei

S=a+b

Date de iesire (S)

afiseaza S

Intelegerea clara a enuntului unei probleme este vitala in rezolvarea acesteia. Daca nu sunt clare care sunt datele de intrare, respectiv cele de iesire ale unui algoritm, cel mai probabil rezolvarea nu va fi corecta, sau vom rezolva o alta problema decat cea ceruta.

2.2 Citirea si afisarea datelor

In subcapitolul anterior am citit datele de intrare ale algoritmului, respectiv am afisat rezultatul. In cazul descrierii algoritmului in cuvinte, acest lucru a fost realizat prin simpla enuntare a operatiei de executat (scrie S). In cazul in care dorim implementarea algoritmului intr-un limbaj de programare, in cazul nostru C++, trebuiesc respectate o serie de reguli clare, deoarece compilatorul trebuie sa ”inteleaga” ceea ce dorim sa facem.

Limbajul C++ nu ofera instructiuni de intrare/iesire. In schimb avem la dispozitie biblioteca de functii standard, prin care, putem realiza aceste operatii. Compilatoarele C++ actuale vin cu bilbioteca standard.

Am ales, pentru simplitatea in utilizare, functii1 din cadrul bibliotecii iostream.

Pentru afisare vom folosi constructii de forma:

cout << ce_vrem_sa_afisam; sau:

cout << ceva << altceva << ... << ultima_chestie;

1 De fapt sunt obiecte, dar in acest moment ce sa zic... asteptati volumele viitorre ale lucrarii pentru explicatii suplimentare (merge si asa ;)), sau vezi [1], [3].

- 7 -

Introducere in ANSI C++

P2. Afisati textul "text de test!" pe ecran.

Linia a doua din sursa va face ca folosirea specificarii spatiului de nume std (std::cout) sa nu fie obligatorie.

In cazul in care folositi mediul Borland C++ 3.1 inlocuiti

cu

in rest programul ramand nechimbat.

Pentru citire vom folosi constructii de forma:

cin >> in_ce_citim; sau:

cin >> v1 >> v1 >> ... >> vn ;

Exemple de citire vom da mai tarziu, dupa introducerea notiunii de variabila.

- 8 -

#include <iostream>using namespace std;

#include <iostream.h>

// P2_1. Afisarea textului 'text de test!' pe ecran.

#include <iostream>using namespace std;

int main(){ cout << "text"; //afisarea unui text cout << " de"; cout << " te" << "st!";}

Introducere in ANSI C++

2.3 Tipuri de date. Variabile si constante.

Algorimii lucreaza cu date. In exemplul cu suma, datele de lucru au fost a,b si S. Datele cu care lucreaza algoritmii au ca si corespondent in C++ constantele si variabilele. Fiecare constanta sau variabila din C++ are asociata un tip.

Tipurile fundamentale ale C++ sunt prezentate in Tabelul 2.1:

Tip Constante Observatii

void - - indica absenta informatiei

char

'A''B''a''+''3'

- defineste un caracter- in memoria calculatorului, stocarea unui caracter se face folosind codul lui ASCII- poate fi folosit si pentru a memora numere intregi mici

int3

123-1020

- numar intreg cu semn- intervalul acoperit de acest tip poate diferi de la un calculator la altul

float3.0

123.456- 100.001

- folosit pentru memorarea numerelor reale- este memorat in format cu virgula mobila (float) in simpla precizie

double3.0

123.456- 100.001

- numere reale- il folosim pentru numere foarte mari sau pentru o precizie mai mare dupa virgula

bool1 0

true false- tip care apare in ANSI C++- nu este recunoscut in Borland C++ 3.1

Tabelul 2.1 – Tipurile de baza in C++

- 9 -

Introducere in ANSI C++

Pe langa aceste tipuri de baza, C++ ofera asa numitii modificatori de tip, folositi pentru a scimba domeniul valorilor pe are le poate pastra o variabila, prezentati in Tabelul 2.2:

Modificator Efect

signed - forteaza existenta semnului

unsigned - daca dorim doar memorarea a valori pozitive intr-o variabila- prin disparitia semnului, este dublat numarul de valori pozitive de memorat

short - reduce intervalul de reprezentare

long - mareste intervalul de reprezentare

Tabelul 2.2 – Modificatorii de tip

Tipurile de date sunt folosite pentru a defini variabilele cu care dorim sa lucram in program.

Pe langa constantele prezentate in Tabelul 2.1, vom folosi si constante de tip sir de caractere, reprezentand succesiuni de caractere cuprinse intre ghilimele. Exemplu de siruri de caractere pot fi: ”ABC”, ”Sunt un sir de caractere!”, ”1+2=3”.

Prin variabila intelegem o entitate capabila sa-si modifice valoarea. In C++, inainte de a folosi o variabila, aceasta trebuie declarata. Forma generala a unei declaratii este:

tip nume; sau:

tip v1, v2, ..., vn;

In momentul declaratiei, C++ permite initializarea variabilei, declaratia devenind:

tip nume=valoare;

Folosirea cuvantului cheie const inainte de declaratia unei variabile va spune compilatorului ca valoarea acesteia nu poate fi modificata in cadrul programului.

- 10 -

Introducere in ANSI C++

const tip  nume=valoare;

Pentru exemplificare fie urmatoarea problema:

P3. Definiti cel putin 5 variabile de tipuri diferite, initializati-le si apoi afisati

valoarile pe ecran.

In urma executiei sa va afisa pe ecran:

Sunt caracterul: A

Sunt intregul: ­123

Sunt intregul mare: 12356789

Sunt intregul fara semn: 98

Sunt intregul mare fara semn: 12345678

Sunt numarul real: 3.14

Numar real (da' mai mare, daca vreau): ­21.344

Pentru „cosmetizarea” afisarii am folosit cout << endl pentru a trece la rand nou. In cazul in care lipseste tipul in cadrul declaratiei si punem doar modificatorul, acesta va fi aplicat pe tipul int, deci unsigned, respectiv unsigned int reprezinta aceeasi declaratie.

Pentru a determina cantitatea de spatiu ocupata de catre fiecare tip de data, introducem operatorul sizeof, operator care returneaza numarul de octeti ocupati

- 11 -

#include <iostream>using namespace std;

int main(){char c='A'; // tip caracter

int i=-123; // intreg "obisnuit" long int l=12356789; // intreg "mare" unsigned int u=98; // intreg pozitiv unsigned long int U=12345678; // intreg mare fara semn float f=3.14; // numar real double d=-21.344; // alt numar real cout << "Sunt caracterul: " << c << endl; cout << "Sunt intregul: " << i << endl; cout << "Sunt intregul mare: " << l << endl; cout << "Sunt intregul fara semn: " << u << endl; cout << "Sunt intregul mare fara semn: " << U << endl; cout << "Sunt numarul real: " << f << endl; cout << "Numar real (da' mai mare, daca vreau): "<<d<< endl;}

Introducere in ANSI C++

in memoria calculatorului.

P4. Verificati cat ocupa in memoria calculatorului dumneavoastra diverse tipuri

de date.

Programul va afisa:

Un caracter ocupa 1 octet

Un intreg ocupa 4 octeti

Tipul float ocupa 4 octeti

Tipul double ocupa 8 octeti

Rezultatele pot sa difere de la calculator la calculator.

In acest moment avem toate datele pentru a implementa algoritmul cerut in problema 1 (cea de la inceputul capitolului). O posibila implementare este:

- 12 -

#include <iostream>using namespace std;

int main(){int i=123;

cout << "Un caracter ocupa "<<sizeof(char)<<" octet" << endl;cout << "Un intreg ocupa " << sizeof(i) << " octeti" << endl;cout <<"Tipul float ocupa "<<sizeof(float)<<" octeti"<< endl;cout <<"Tipul double ocupa "<<sizeof(double)<<" octeti";

}

// P2_4. Se citesc doua anumere intregi. Afisati pe ecran suma lor.

#include <iostream>using namespace std;

int main(){ int a,b,S; // declararea variabilelor cin >>a; cin >>b; // citirea datelor de intrare

S=a+b; // calcularea sumei

cout << S; //afisarea rezultatului}

Introducere in ANSI C++

La rularea aplicatiei avem impresia ca ea s-a blocat, avand in fata doar un cursor care clipeste. Motivul este cel ca, dupa declaratii, se executa instructiunea cin>>a; iar programul se opreste din executie in asteptarea unei valori introduse de la tastatura. Acest lucru se realizeaza prin scrierea unei valori urmata de apasarea tastei Enter. In acest moment variabila a preia valoarea introdusa, apoi se trece la urmatoarea instructiune. Tratand analog si cea de a doua citire, obtinem in final afisarea sumei valorilor introduse.

O posibila rulare a aplicatiei va arata de forma:

3

5

8

Observam ca varianta prezentata nu arata deloc bine, o „cosmetizare” fiind bine venita:

P5. Calculati valoare expresiei 2*(a-b)+c, realizand o interfata prietenoasa cu

utilizatorul.

In acest moment, executia va afisa ceva de forma:

a=3

b=5

c=2

E=2*(3­5)+2=­2

- 13 -

#include <iostream>using namespace std;

int main(){int a,b,c,E; // declararea variabilelor

cout << endl; // trecem la rand nou cout << "a="; // scriem un mesaj ajutator cin >>a; // citim prima variabila

cout << "b="; cin >>b; // analog pentru celelalte cout << "c="; cin >>c; // date de intrare

E=2*(a-b)+c; // calcularea expresiei

cout << "E=2*(" << a << "-"; //afisarea rezultatuluicout << b << ")+" << c << "=" << E;

}

Introducere in ANSI C++

2.4 Secvente escape

Secventele escape sunt succesiuni de caractere care incep cu caracterul '\', fiecare secventa fiind interpretata ca un singur caracter special. Secventele escape sunt folosite pentru a introduce entitati imposibil de afisat in alt mod, cum ar fi trecerea la rand nou sau tab-ul.

Tabelul 2.3 da o serie de secvente escape uzuale.

Secventa escape Efect

\n - trecere la rand nou- echivalent cu endl

\t - tab orizontal

\' - un caracter ' (apostrof)

\\ - un caracter \ (backslash)

\” - un caracter ” (ghilimea)

\b - backspace

\a - sunet de alerta

\nnn - nnn reprezinta un cod ASCII in baza 8

Tabelul 2.3 – Secvente escape

Secventele escape pot aparea sub forma de caractere de sine statatoare, de forma '\n' sau '\\', sau in cadrul unor siruri de caractere, cum ar fi ” abc\n\”abc\” ”.

Urmatoarele doua surse rezolva aceeasi problema:

- 14 -

Introducere in ANSI C++

P6. Afisati un romb din caracterul '$' pe ecran.

   $

  $$$

 $$$$$

  $$$

   $

   $

  $$$

 $$$$$

  $$$

   $

- 15 -

#include <iostream>using namespace std;

int main(){ cout << " $ " << endl; cout << " $$$ " << endl; cout << " $$$$$ " << endl; cout << " $$$ " << endl; cout << " $ " << endl;}

#include <iostream>using namespace std;

int main(){ cout << " $ \n $$$ \n $$$$$ \n" << endl; cout << " $$$ \n $ " << endl;}

Introducere in ANSI C++

2.5 Referinte (alias-uri)

C/C++ permite definirea unor alias-uri ale variabilelor. Forma generala este:

tip a;

tip &porecla=a;

Din momentul definirii unei referinte, variabila initiala va putea fi accesata atat cu numele ei cat si cu alias-ul. Folosirea referintelor reluata in Capitolul 8, referitor la functii.

P7. Exemplu de lucru cu referinte.

Sunt variabila a:123

Sunt tot variabila a:123

Sunt a, si mi se spune:123

- 16 -

#include <iostream>using namespace std;

int main(){ int a=123; // variabila intreaga a int &copie_a=a; // un alt nume pentru a int &porecla=a; // tot a cout << "Sunt variabila a:" << a << endl; cout << "Sunt tot variabila a:" << copie_a << endl; cout << "Sunt a, si mi se spune:" << porecla << endl;}

Introducere in ANSI C++

2.6 Probleme propuse

Pp1. Afisati pe ecran un bradulet folosind caracterul '*'.

Pp2. Cititi de la tastatura trei numere intregi, apoi afisati-le in ordine inversa, cate unul pe un rand.

Pp3. Calculati media aritmetica a doua valori intregi citite de la tastatura. Citirea si afisarea sa fie prietenoasa cu utilizatorul.

Pp4. Calculati si afisati, in acelasi program si intr-o varianta prietenoasa cu utilizatorul, urmatoarele expresii:

M(a,b,c) = (a+b+c)/3

F(a,b) = (a+3)/2 +b

E(a,b,c,d) = (a+b)/2 + (d­c)/3

Pp5. Folositi secvente escape pentru a afisa pe ecran:

Ma numesc ”ghilimea” si nu arat asa '.

Eu, adica \, sunt un backslash. 

Acum voi face beep de 3 ori, dupa ce am 'sarit' un tab.

- 17 -

Introducere in ANSI C++

Capitolul 3 – Expresii C++

Numim expresie valida in C++ orice succesiune corecta de variabile, constante, operatori si apeluri de functii evaluabila la o valoare unica.

3.1 Atribuirea

Forma generala a operatiei de atribuire este:

variabila = expresie;

Modul in care limbajul realizeaza atribuirea este urmatorul:

Pas 1. Este evaluata valoarea expresiei din dreapta (daca variabila din stanga apare in cadrul expresiei, va fi folosita valoarea curenta)

Pas 2. Valoarea calculata este atribuita variabilei din stanga

P1. Exemple de atribuiri simple

La rulare vom avea:

i=3

c=C

f=3.14

Interesant este faptul ca in C/C++, spre deosebire de alte limbaje, atribuirea este o expresie, deci pe locul in care folosim atribuirea apare o valoare. Valoarea

- 18 -

#include <iostream>using namespace std;

int main(){ int i; char c; float f; i=3; // intregul i ia valoarea 3 c='A'+2; // caracterul c ia valoarea 'C' (65) f=2+1.14; // realul f ia valoarea 3.14 cout << "\ni=" << i; cout << "\nc=" << c; cout << "\nf=" << f;}

Introducere in ANSI C++

returnata de catre oparatia de atribuire este valoarea expresiei din dreapta. Deci, daca avem atribuirea a=3, pe locul atribuirii apare valoarea 3. Putem folosi acest fapt pentru a realiza atribuiri multiple, de forma urmatoare:

variabila1 = variabila2 = ... = variabilan = expresie;

P2. Alte exemple de atribuiri

Se va afisa:

Rezultatul atribuirii i=3 este:3

i=8

a=10

b=10

Pentru expresia a=b=i+2 avem urmatoarea ordine a operatiilor:

Pas 1. Initial avem a = expresie, unde expresie este b=i+2.

Pas 2. Se evalueaza expresie:

- variabila b ia valoarea i+2 (10 in exemplul nostru)

- expresie returneaza valoarea 10

Pas 3. variabila a ia valoarea 10

- 19 -

#include <iostream>using namespace std;

int main(){ int i; int a,b; cout << "Rezultatul atribuirii i=3 este:" << (i=3); i=i+5; // in partea dreapta i are valoare 3, apoi devine 8 a=b=i+2;// a si b vor lua valoarea 10 (i nu se modifica) cout << "\ni=" << i; cout << "\na=" << a; cout << "\nb=" << b;}

Introducere in ANSI C++

3.2 Conversii de tip

Este posibil ca tipul expresiei sa nu fie identic cu tipul variabilei unde memoram rezultatul, caz in care este realizata o conversie implicita de tip si, in unele cazuri, vom avea un avertisment la compilare. In general, conversia de tip este realizata fara probleme daca variabila si expresia sunt ambele de tip intreg (int, char, long etc.) sau de tip real (float, double etc.), iar variabila este reprezentata pe un numar egal sau mai mare de octeti decat rezultatul expresiei.

In urmatoare doua exemple vom evidentia cele doua cazuri de conversie implicita.

P3. Conversie implicita de tip, fara pierdere de informatie

i=65

c=A

f=12345

- 20 -

#include <iostream>using namespace std;

int main(){ int i=12345; char c='A'; float f=97.14; f=i; i=c; cout << "\ni=" << i; cout << "\nc=" << c; cout << "\nf=" << f; }

Introducere in ANSI C++

P4. Conversie implicita de tip, cu pierdere de informatie

La compilare, avem:

p04.cpp: In function `int main()':

p04.cpp:11: warning: converting to `int' from `float'

La rulare:

i=97

c=9

f=97.14

Observatie: c contine caracterul '9', nu valoarea intreaga 9.

C si C++ permit conversii explicite de tip, de forma:

(tip) expresie sau:

tip(expresie)

- 21 -

#include <iostream>using namespace std;

int main(){ int i=12345; char c='A'; float f=97.14; c=i; i=f; cout << "\ni=" << i; cout << "\nc=" << c; cout << "\nf=" << f; }

Introducere in ANSI C++

P5. Conversia explicita de tip

i=12348

c=a

f=97

O aplicatie directa a conversiei explicite de tip este:

P6. Se citeste un caracter. Afisati codul ASCII corespunzator caracterului citit

Doua posibile rulari ale aplicatiei:

Dati caracterul:a

Caracterul 'a' are codul ASCII 97

Dati caracterul:9

Caracterul '9' are codul ASCII 57

- 22 -

#include <iostream>using namespace std;

int main(){ int i=12345; float f=97.14; cout << "\ni=" << float(i+3); cout << "\nc=" << (char) 97; cout << "\nf=" << int(f); }

#include <iostream>using namespace std;

int main(){ char c; cout << "Dati caracterul:"; cin >>c; cout << "Caracterul \'" << c; cout << "\' are codul ASCII " << int(c);}

Introducere in ANSI C++

3.3 Expresii aritmetice

Forma generala este, in functie de operator:

operator operand sau

operand1 operator operand2

Operatorii uzuali folositi in cadrul expresiilor aritmetice si care se comporta asa cum ne asteptam sunt '+', '­', '*'.

Operatorul '/' (impartirea) reactioneaza in mod diferit in functie de tipul operanzilor. Astfel, daca cel putin unul dintre operanzi este de tip real, rezultatul este real. Daca ambii operanzi sunt intregi, este realizata impartirea intreaga. In acest ultim caz are sens sa vorbim despre restul impartirii, dat de operandul '%' (modulo). Operatorul modulo nu poate fi aplicat tipurilor reale.

Urmatoare problema da exemple de folosire a impartirii:

P7. Expresii aritmetice. Impartirea reala si intreaga.

 ­123

123/246=0

123/246=0.5

In cazul in care dorim ca raportul a doua expresii de tip intreg sa dea o valoare reala vom folosi conversia explicita de tip, de forma:

float(operator_intreg1) / (operator_intreg2)

In acest context, 7/2 are valoarea 3, dar 7./2 sau float(7)/2 au valoarea 3.5.

- 23 -

#include <iostream>using namespace std;

int main(){ int i=123,a; float f; cout << -i <<"\n"; // operatorul unar - a=2*i; // operatorul binar * f=a; cout << i << "/" << a << "=" << i/a << "\n";

// impartire intrega cout << i << "/" << f << "=" << i/f; // impartire reala}

Introducere in ANSI C++

Exista o serie de operatori de atribuire speciali (forma scurta a atribuirii), operatori ce vor avea forma generala:

 variabila op= expresie

unde op poate fi orice operator aritmetic prezentat anterior.

Atribuirea este echivalenta cu:

 variabila = variabila op expresie

P8. Forma scurta a atribuirii

125     100     200     20      

Alti operatori, specifici C si C++, sunt cei de incrementare si decrementare:

variabila++

++variabila

variabila­­

variabila­­

Operatorul ++ realizeaza incrementarea variabilei pe care este aplicat, adica este similar atribuirii variabila=variabila+1, in timp ce –­ realizeaza decrementarea, adica variabila=variabila­1.

Diferenta intre cele doua forme (sufix sau prefix), consta in faptul ca, in cazul sufix, valoarea variabilei este folosita neschimbata in expresia din care face parte, apoi aceasta este incrementata, in timp ce in cazul prefix, valoarea variabilei este incrementata, apoi este folosita in expresia din care face parte.

- 24 -

#include <iostream>using namespace std;

int main(){ int i=123; i+=2; cout << i << "\t"; i-=25; cout << i << "\t"; i*=2; cout << i << "\t"; i/=10; cout << i << "\t";}

Introducere in ANSI C++

P9. Incrementarea si decrementarea

126     125     125     

3.4 Expresii logice

Sunt acele expresii in care rezultatul este interpretat ca adevarat sau fals.

Daca in ANSI C++ avem definit un tip specific (bool), compilatoarele mai vechi (bcc) nu suporta acest tip. In orice caz, regula care se respecta este faptul ca valoarea 0 inseamna fals din punct de vedere logic, iar orice valoare diferita de 0 (de obicei valoarea 1) este interpretata ca adevarat, asa cum arata si exemplul urmator.

P10. Valoarea expresiilor logice

true=1

false=0

- 25 -

#include <iostream>using namespace std;

int main(){ int i=123,a,b; i++; // daca nu apare intr-o expresie este similar cu ++i; a=++i;// incrementam i, apoi il atribuim lui a b=i++;// atribuim lui a valoarea lui i, apoi incrementam i cout << i << "\t" << a << "\t"<< b << "\t";}

#include <iostream>using namespace std;

int main(){ bool b; b=true; cout << "true=" << b << "\n"; b=false; cout << "false=" << b;}

Introducere in ANSI C++

Un caz particular de expresii logice il reprezinta expresiile relationale, expresii in care sunt comparate doua valori. Operatorii relationali sunt dati in tabelul urmator:

Operator Semnificatie Exemplu

< mai mic 3<5

> mai mare (4+1) > (2*2)

<= mai mic sau egal 2<=3

>= mai mare sau egal 3>=3

!= diferit 2!=3

== egal 3==3

Tabelul 3.1 – Operatorii relationali

Observam ca testarea egalitatii a doua expresii nu se face folosind '=', operator reprezentand atribuirea.

Diferite expresii logice pot fi reunite folosind operatorii logici din Tabelul 3.2.

Operator Semnificatie Exemplu

&& si logic (x>=2)&&(x<=5), adica x [2,5]∈

|| sau logic (x<2)||(x>5), adica x∉[2,5]

! negatie logica !(x>=2), adica x<2

Tabelul 3.2 – Operatorii logici

- 26 -

Introducere in ANSI C++

P11. Se citesc trei valori intregi de la tastatura. Verificati unde se gaseste prima

valoare citita in raport cu intervalul determinat de ultimele doua valori.

Dati intervalul [a,b]

a=2

b=5

apartine intervalului:1

nu apartine intervalului:0

Observam ca valoarea de adevar a afirmatiilor este data sub forma 0-fals si 1-adevarat.

3.5 Operatorul conditional

Are forma generala:

expresie1?expresie2:expresie3

Daca expresie1 este evaluata la adevarat, expresia returneaza expresie2, altfel returneaza expresie3.

In unele cazuri particulare poate fi un inlocuitor pentru instructiunea if.

- 27 -

#include <iostream>using namespace std;

int main(){ int i,a,b; cout << "Dati valoarea:"; cin >>i; cout << "Dati intervalul [a,b]\na="; cin >> a; cout << "b="; cin >> b; cout << "apartine intervalului:" << (i>=a)&&(i<=b)); cout << "\nnu apartine intervalului:" << (i<a)||(i>b);}

Introducere in ANSI C++

P12. Calculati expresia E(x)=1/x daca e posibil, afisand valoarea 0 in caz

contrar.

x=2

1/2 = 0.5

P13. Calculati expresia max(a,b), unde a si b sunt valori reale citite de la

tastatura.

a=5

b=2

max(5,2)=5

P14. Se citesc doua valori intregi de la tastatura. Interschimbati cele doua

valori, apoi afisati rezultatul pe ecran.

- 28 -

#include <iostream>using namespace std;

int main(){ int x; float E; cout << "x="; cin >>x; E = (x!=0) ? 1./x : 0; cout << "1/" << x << " = " << E;}

#include <iostream>using namespace std;

int main(){ float a,b,max; cout << "a="; cin >>a; cout << "b="; cin >>b; max = (a>b) ? a : b; cout << "max(" << a << "," << b << ")=" << max;}

Introducere in ANSI C++

a=2

b=5

­­ dupa interschimbare ­­

a=5     b=2

Ordinea de efectuare a operatiilor in cazul unei expresii complexe este data de prioritatea operatorilor, respectiv de folosirea parantezelor. Tabele cu prioritatea operatorilor gasiti in [1],[5] etc. De cate ori nu stiti exact ordinea in care se vor executa diferite operatii intr-o expresie, folositi parantezele.

P15. Necesitatea folosirii parantezelor. Acest program NU va verifica

apartenenta valorii a intervalului [1,5], desi in unele cazuri pare sa functioneze !!!

a=8

8 apartine intervalului [2,5]:1

- 29 -

#include <iostream>using namespace std;

int main(){ int a,rez; cout << "a="; cin >> a; rez=(2<=a<=5);// rez=((2<=a)&&(a<=5)); cout << a << " apartine intervalului [1,5]:" << rez ;}

#include <iostream>using namespace std;

int main(){ int a,b,t; cout << "a="; cin >>a; cout << "b="; cin >>b; t=a; a=b; b=t; cout << "-- dupa interschimbare --\n"; cout << "a=" << a << "\t" << "b=" << b ;}

Introducere in ANSI C++

Observam ca la conditia care pare "8 apartine lui [2,5]" rezultatul este adevarat (1). Motivul este urmatorul:

Expresia 2<=a<=5 este evaluata de la stanga la dreapta, deci primul lucru evaluat este 2<=a, adevarat pentru a=8, adica 1 in context C/C++. Urmeaza apoi comparatia 1<=5, deci rezultatul final este adevarat. Rezolvarea corecta este cea data in comentariu.

3.6 Operatorul virgula

Are forma generala:

expresie1 , expresie2,... , expresien 

Expresiile sunt evaluate de la deapta la stanga, valoarea returnata la final fiind valoarea si tipul dat de expresien.

P16. Simulati operatorii de preincrementare si postincrementare folosind

operatorul virgula (b=a++ si b=++a).

a=3

4 3

5 5

- 30 -

#include <iostream>using namespace std;

int main(){ int a,b,t; cout << "a="; cin >> a; b=(t=a,a+=1,t); // b=a++ cout << a << " " << b << "\n"; b=(a+=1,a); // b=++a cout << a << " " << b << "\n";}

Introducere in ANSI C++

3.7 Operatori care actioneaza la nivel de bit

Tabelul 3.3 da operatorii pe biti acceptati in C/C++:

Operator Semnificatie Exemplu

& si pe bitix&1 – reseteaza toti bitii, in afara celui mai putin semnificativ

| sau pe bitix|1 – seteaza bitul cel mai putin semnificativ

^ sau exclusivx^0 – complementeaza toti bitiix^x – valoare nula pentru orice x

~ negatie pe biti ~x – complementeaza toti bitii

>> deplasare la dreapta x<<2 – similar cu x*=2

<< deplasare la stanga x>>3 – similar cu x/=3

Tabelul 3.3 – Operatori logici pe biti

Exemplele din tabel sunt date pentru x de tip intreg.

- 31 -

Introducere in ANSI C++

3.8 Probleme propuse

Pp1. Se citeste un numar real x, strict pozitiv, de la tastatura. Calculati si afisati

rezultatul expresiei (x-1)/x2 .

Pp2. Se citeste un numar real de la tastatura. Folosind conversii de tip, afisati partea fractionara a numarului.

Pp3. Se citesc doua numere a si b de la tastatura, valori intregi strict pozitive. Afisati rezultatul expresiei E(a,b)=(a-b)/(a+b).

Pp4. Folosind operatorul conditional, determinati minimul a doua valori intregi citite de la tastatura.

Pp5. Se citeste un numar real x de la tastatura. Folosind operatorul conditional, calculati si afisati rezultatul expresiei E(a)=max(min(a,7),|a-7|).

Pp6. Se citesc trei numere intregi de la tastatura. Afisati valoarea "din mijloc", adica valoarea care nu este nici minimul, nici maximul celor trei valori date.

Pp7. Se citesc trei numere intregi de la tastatura. Afisati pe ecran suma celor mai mici doua valori.

- 32 -

Introducere in ANSI C++

Capitolul 4 – Instructiuni conditionale

Daca dorim executia unor instructiuni doar daca o conditie este adevarata sau daca dorim ramificarea executiei programului, folosim instructiuni conditionale.

4.1 Instructiunea if

Exista doua forme ale instructiunii if:

if (expresie) instructiune

In acest caz, daca expresie este evaluata la adevarat (o valoare nenula), atunci se executa instructiunea care urmeaza.

if (expresie) instructiune1

else instructiune2

Daca expresie este evaluata la adevarat se executa instructiune1, altfel se executa instructiune2.

De obicei expresia dintre paranteze este o expresie logica, dar, datorita conventiilor C referitoare la notiunile de adevarat si fals, expresia poate fi de orice tip, inclusiv expresii aritmetice sau atribuiri. In urmatoarele exemple vom arata diverse moduri de a realiza comparatii in C.

P1. Se citeste un numar intreg de la tastaura. Verificati daca acesta este nul,

pozitiv sau negativ.

a=123

123 este pozitiv.

- 33 -

#include <iostream>using namespace std;

int main(){ int a; cout << "a="; cin >> a; if (a==0) cout << a << " este nul."; if (a<0) cout << a << " este negativ."; if (a>0) cout << a << " este pozitiv.";}

Introducere in ANSI C++

sau:

a=0

0 este nul.

P2. Se citeste un numar intreg de la tastaura. Verificati daca acesta este nul sau

nu.

a=123

123 este nenul.

Sa vedem putin ce se intampla in acest caz. Conditia "ciudata" din if, va fi evaluata in felul urmator:

- daca a este diferit de zero, atunci, conform conventiilor C, reprezinta true, deci se va executa prima ramura a if-ului

- daca a este nul, adica corespunde valorii false, se executa cea de a doua ramura.

In general, verificarea unei expresii daca este adevarata sau falsa, se realizeaza prin simpla scriere a expresiei intre parantezele if-ului, de forma:

if (expresie)

Nu este nevoie sa scriem varianta echivalenta:

if (expresie != 0) sau

if (expresie != false)

- 34 -

#include <iostream>using namespace std;

int main(){ int a; cout << "a="; cin >> a; if (a) cout << a << " este nenul."; else cout << a << " este nul.";}

Introducere in ANSI C++

P3. Se citesc de la tastaura doua numere intregi si un caracter reprezentand

operatiile matematice '+', '-'. Afisati valoarea corespunzatoare aplicarii operatiei

date asupra intregilor cititi.

a=2

b=3

operatia:+

2+3=5

In problema 3, observam ca instructiunea de pe ramura fals a primului if este o alta instructiune if. Vom denumi acest caz ca imbricarea instructiunii if si vom folosi alinierea else cu if-ul corespunzator pentru a imbunatatii lizibilitatea programului.

Daca dorim executia a mai multe instructiuni pe o ramura a instructiunii if, vom

grupa aceste instructiuni intr-un bloc (instructiune compusa). Vom numi bloc zona

cuprinsa intre acolade ({...}). In cazul scrierii instructiunilor intr-un bloc, acestea vor fi tratate in mod unitar, ca o singura instructiune. In legatura cu sugestii privind indentarea codului studiati Anexa 3.

- 35 -

#include <iostream>using namespace std;

int main(){ int a,b; char op; cout << "a="; cin >> a; cout << "b="; cin >> b; cout << "operatia:"; cin >> op; if (op=='+') cout << a << '+' << b << '=' << a+b; else if (op=='-') cout << a << '-' << b << '=' << a-b; else cout << "Operatiei neimplementata!"; }

Introducere in ANSI C++

P4. Se citesc de la tastaura trei numere intregi. Calculati maximul si minimul

valorilor.

a=2

b=3

c=1

min(2,3,1)=1

max(2,3,1)=3

Datorita faptului ca putem avea mai multe nivele de imbricare a instructiunilor if, unele avand else si altele nu, trebuie acordata o atentie deosebita corespondentei if­else dorite. Fiecare else va fi in corespondenta cu primul if care se gaseste inaintea lui in textul sursa cu proprietatea ca nu ii corespunde nici un else si nu este inclus in instructiunea if care il precede.

Mai mult, o aliniere corecta nu va tine locul de bloc.

- 36 -

#include <iostream>using namespace std;

int main(){ int a,b,c; int max,min; cout << "a="; cin >> a; cout << "b="; cin >> b; cout << "c="; cin >> c;// calculam minimul si maximul primelor doua valori if (a>b){ min=b; max=a; } else{ min=a; max=b; }// "ajustam" minimul si maximul in functie de a treia valoare if (c>max) max=c; if (c<min) min=c; cout << "min("<<a<<","<<b<<","<<c<<")="<<min; cout << "\nmax("<<a<<","<<b<<","<<c<<")="<<max;}

Introducere in ANSI C++

P5. Exemple de erori de compilare la surse care par corecte.

p05.cpp: In function `int main()':

p05.cpp:12: error: syntax error before `else'

Desi, datorita imbricarii, pare ca pe ramura true vor fi executate cele doua instructiuni, compilatorul va interpreta if-ul ca fiind fara else (pe ramura true se executa o singura instructiune), deci nu va gasi corespondet pentru else. Linia [2] din cod ar trebui aliniata cu instruciunea if, afisarea fiind executata in afara acestuia.

P6. Varianta corecta a problemei 5.

a=2

E mai mic ca trei

Nu mai da eroare ;)

Acum liniile [1] si [2] sunt grupate intr-un bloc, deci pe ramura true se executa o singura instructiune, iar else isi gaseste corespondent.

- 37 -

#include <iostream>using namespace std;

int main(){ int a; cout << "a="; cin >> a; if (a<3) cout << "E mai mic ca trei\n"; //[1] cout << "Pacat doar ca nu va merge niciodata..."; //[2] else cout << "E mai mare ca trei"; }

#include <iostream>using namespace std;

int main(){ int a; cout << "a="; cin >> a; if (a<3){ cout << "E mai mic ca trei\n"; //[1] cout << "Nu mai da eroare ;) "; //[2] } else cout << "E mai mare ca trei"; }

Introducere in ANSI C++

In multe probleme, folosirea expresiilor logice compuse reprezinta o alegere evidenta.

P7. Determinati daca patru numere intregi citite de la tastatura sunt in ordine

crescatoare sau nu.

Introduceti patru numere intregi:

2 4 5 8

Numerele citite sunt in ordine crescatoare

Putem folosi o variabila booleana pentru a memora rezultatul unor exepresii logice complexe, apoi sa folosim acea variabila ca o conditie in cadrul instructiunii if. Acelasi efect il putem obtine folosind o variabila de tip intreg in locul celei booleene.

P8. Determinati daca patru numere intregi formeaza o progresie aritmetica.

Daca da, afisati ratia.

- 38 -

#include <iostream>using namespace std;

int main(){ int a,b,c,d; cout << "Introduceti patru numere intregi:\n"; cin >> a >> b >> c >> d; if ((a<=b) && (b<=c) && (c<=d)) cout << "Numerele citite sunt in ordine crescatoare"; else cout << "Numerele citite nu sunt in ordine crescatoare";}

Introducere in ANSI C++

Introduceti patru numere intregi:

2 4 6 8

Numerele citite sunt in progresie

aritmetica, cu ratia: 2

Un caz uzual in care apare folosirea conditiei multiple este verificarea apartenentei unui numar unui interval dat.

P9. Calculati urmatoarea functie:

f x =x pentru x∈[10,20 ]

−x pentru x∈−20,00   inrest

- 39 -

#include <iostream>using namespace std;

int main(){int a,b,c,d;bool progresie;cout << "Introduceti patru numere intregi:\n";cin >> a >> b >> c >> d;

progresie = (2*b==a+c); // verificam daca primele trei

formeaza o progresieprogresie = progresie && (2*c==b+d); // acum

urmatoarele...

if (progresie){cout << "Numerele citite sunt in progresie\n";cout << "aritmetica, cu ratia: " << b-a;

}else

cout << "Nu avem o progresie aritmetica";}

Introducere in ANSI C++

x=3

f(3)=0

Nu este recomandata verificarea egalitatii intre doua numere reale. Datorita erorilor de calcul, este posibil ca rezultatele calculelor sa nu fie exacte. In acest caz se recomanda folosirea unor conditii de forma abs(a­b) < eps, unde eps este o valoare mica.

4.2 Instructiunea switch

In unele aplicatii dorim sa comparam valoarea unei variabile sau a unei expresii cu mai multe valori sau expresii constante. In acest caz solutia oferita de catre instructiunile if imbricate poate fi greoaie, instructiunea switch fiind mai eleganta.

Forma generala a instructiunii switch este:

switch (expresie){

case constanta1:  instructiuni;

case constanta2:  instructiuni;

. . . 

case constantan:  instructiuni;

default: instructiune;

}

Optiunea default este optionala.

Executia instructiunii are loc in urmatorul mod:

- 40 -

#include <iostream>using namespace std;

int main(){ int x,F; cout << "x="; cin >> x; if ((x>=10)&&(x<=20)) F=x; else if ((x>-20)&&(x<0)) F=-x; else F=0;

cout << "f(" << x << ")=" << F;}

Introducere in ANSI C++

- se evalueaza expresie, apoi se compara, pe rand cu fiecare constanta

- daca este realizata egalitatea, se executa setul de instructiuni ce urmeaza

- daca dupa executarea unei secvente dorim iesirea din switch, se foloseste instructiunea break

Cum de obicei se intampla acest lucru, forma generala devine:

switch (expresie){

case constanta1:  instructiuni; break;

case constanta2:  instructiuni; break;

. . . 

case constantan:  instructiuni;

default: instructiune;

}

P10. Se citesc de la tastatura doua numere intregi si un caracter. Daca acest

caracter este ‘+’, ‘-’, ‘*’ sau ‘/’, realizati operatia respectiva intre intregiii de

intrare, apoi afisati rezultatul pe ecran.

- 41 -

#include <iostream>#include <math.h>using namespace std;

int main(){int a,b;

float R; char op; cout << "a="; cin >>a; cout << "b="; cin >>b; cout << "operator:"; cin >>op; switch (op){ case '+': R=a+b; break; case '-': R=a-b; break; case '*': R=a*b; break; case '/': if (b!=0) { R=float(a)/b; break;} else {cout << "Impartire la zero!"; return(1);} default:cout << "Operator necunoscut!"; return(1); } cout << a << op << b << "=" << R;}

Introducere in ANSI C++

a=2

b=4

operator:/

2/4=0.5

P11. Se citeste de la tastatura ziua din saptamana corespunzatoare date de 1 ale

unei luni (1 – pentru luni, 2 – pentru marti, etc). Afisati ce zi din saptamana va

avea o alta data a lunii respective.

Ziua corespunzatoare datei de 1 a lunii:0

Data de verificat:14

Data corespunde unei zi de DUMINICA

- 42 -

#include <iostream>using namespace std;

int main(){ int prima_zi, zi; cout << "Ziua corespunzatoare datei de 1 a lunii:"; cin >>prima_zi; cout << "Data de verificat:"; cin >> zi; zi=(zi+prima_zi-1)%7; cout << "Data corespunde unei zi de "; switch(zi){ case 0: cout <<"luni"; break; case 1: cout <<"marti"; break; case 2: cout <<"miercuri"; break; case 3: cout <<"joi"; break; case 4: cout <<"vineri"; break; case 5: cout <<"SAMBATA"; break; case 6: cout <<"DUMINICA"; break; } }

Introducere in ANSI C++

4.3 Probleme propuse

Pp1. Dati un exemplu de problema in care expresia conditionala nu poate tine locul instructiunii if.

Pp2. Calculati:

f x , y =xy pentru x0x∗y pentru x≥0

Pp3. Se citesc patru numere intregi de la tastatura. Afisati valorile distincte citite.

Pp4. Sa se determine ultimele doua cifre ale produsului a*b, a si b citite de la tastatura.

Pp5. Se citeste x intreg. Sa se calculeze ultima cifra a lui 2x.

Pp6. Rezolvati ecuatia de gradul 1.

Pp7. Se citesc 3 numere intregi de la tastatura. Daca toate nuemerele citite sunt pozitive, afisati-le in ordine crescatoare. Daca nu, afisati mesajul "We have a problem!".

Pp8. Folosind instructiunea switch, afisati numele unei luni cu numarul de ordine citit de la tastatura.

- 43 -

Introducere in ANSI C++

Capitolul 5 – Instructiuni repetitive

Daca dorim executia unor instructiuni de mai multe ori, in functie de o conditie, folosim instructiunile repetitive. Acestea sunt while, do­while si for.

5.1 Instructiunea while

Forma generala a instruciunii while:

while (expresie) instructiune;

Expresia poate fi de orice tip, inclusiv expresii aritmetice sau atribuiri, dar, ca si la instruciunea if, sunt de preferat expresii logice. Daca se doreste executarea unui grup de instructiuni se va folosi un bloc care sa formeze corpul instructiunii while.

Executia instructiunii:

- se evalueaza expresie

- daca rezultatul este true (diferit de 0), se executa instructiune, apoi se revine la primul pas

- daca rezultatul este false (0), se continua executia cu instructiunea urmatoare

Poate cea mai simpla problema care se poate rezolva folosind structuri repetitive este afisarea valorilor intregi din [1,10]. In acest caz, o rezolvare de forma:

... cout << 1 << 2 << 3 << ...

poate fi utilizata doar daca intervalul dat este mic si definit de catre constante.

La afisarea valorilor dintr-un interval [a,b], a,b variabile intregi citite de la tastatura aceasta metoda nu poate fi folosita.

- 44 -

Introducere in ANSI C++

P1. Afisati numerele de la 1 la 10 pe ecran.

 citesc1 2 3 4 5 6 7 8 9 10 

P2. Afisati urmatoarele serii de numere pe ecran (a,b,n sunt intregi citite de la

tastatura, a<b)

• 1 2 4 8 ... - valori mai mici sau egale citesc cu n

• a, a+1, ..., b

• n valori echidistante din intervalul [a,b]

• 1 0 1 0 1 ... - n valori

- 45 -

#include <iostream>using namespace std;

int main(){ int i; i=1; while (i<=10){ cout << i<< " "; i++; }}

Introducere in ANSI C++

n=10

a=2

b=7

1 2 4 8

2 3 4 5 6 7

2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7

1 0 1 0 1 0 1 0 1 0 

- 46 -

#include <iostream>using namespace std;

int main(){ int i,a,b,n; float pas,k; cout << "n="; cin >>n; cout << "a="; cin >>a; cout << "b="; cin >>b; i=1; while (i<=n){ cout << i<< " "; i=2*i; }

cout << endl; i=a; while (i<=b){ cout << i << " "; i++; }

cout << endl; pas=float(b-a)/n; k=a; while (k<=b){ cout << k << " "; k=k+pas; }

cout << endl; i=1; while (i<=n){ cout << i%2 << " "; i++; }

}

Introducere in ANSI C++

In unele cazuri corpul instructiunii while poate sa fie vid. In acest caz vomverondela folosi intructiunea vida ; .

P3. Se citeste un numar intreg de la tastatura. Afisati cel mai mare numar

multiplu de 10 mai mic ca numarul citit. (while fara corp)

n=123

120

5.2 Instructiunea do-while

Forma generala a instruciunii do ­ while:

do 

instructiune;

while (expresie); 

Observatiile de la instructiunea while raman valabile si pentru aceasta instructiune. Diferenta dintre while si do-while este aceea ca, in cazul instructiunii do- while instructiunea se executa cel putin o data, chiar si in cazul in care expresie este falsa la prima iteratie.

Modul de executie al instructiunii:

- se executa instructiune

- se evalueaza expresie

- daca rezultatul este true (diferit de 0) se revine la primul pas

- daca rezultatul este false (0) se incheie instructiunea si se continua programul

- 47 -

#include <iostream>using namespace std;

int main(){ int n; cout << "n="; cin >>n; while ((n--)%10!=0); // while cu corp vid cout << n+1;}

Introducere in ANSI C++

Urmatoarele secvente de instructiuni sunt echivalente:

  if (expresie)      do 

    instructiune;    while (expresie); 

  while (expresie) instructiune;

Figura 5.1

P4. Se citesc de la tastatura doua caractere. Afisati pe ecran caracterele si

codurilor lor ASCII din intervalul dat de catre caracterele citite.

 A C

A ­ 65

B ­ 66

C ­ 67

P5. Se citeste de la tastatura un numar real cu 3 zecimale. "Mutati" virgula pe

toate pozitiile interioare posibile in cadrul numarului initial. Afisati pe ecran

rezultatul, cate un numar pe un rand.

- 48 -

#include <iostream>using namespace std;

int main(){ char c1,c2,t,c; cin >> c1 >> c2; if (c1>c2){t=c1; c1=c2; c2=t;} c=c1; do{ cout << c << " - " << int(c) << endl; c++; }while(c<=c2); }

Introducere in ANSI C++

n=12.789

1278.9

127.89

12.789

1.2789

5.3 Instructiunea for

In general instructiunile while si do-while poarta numele de cicluri cu numar necunoscut de pasi. Instructiunea for este instructiunea ce implementeaza in C ciclul cu numar cunoscut de pasi (desi vom vedea ca nu este limitata la acest tip de cicluri).

Forma generala a instruciunii for:

for(expr1; expr2; expr3) instructiune;

sau, de obicei:

for(init; cond_de_oprire ; reinit) instructiune;

Modul de executie al instructiunii:

- se executa init

- cat timp expresie este adevarata

- 49 -

#include <iostream>using namespace std;

int main(){ float n; cout << "n="; cin >> n; n=n*100; do{ cout << n << endl;; n=n/10; } while(n>1); }

Introducere in ANSI C++

- se executa instructiune

- se executa reinit

Instructiunea for este folosita, in principal, pentru parcurgerea seriilor de numere.

P6. Afisati urmatoarele serii de numere pe ecran (a,b,n sunt intregi citite de la

tastatura, a<b):

– 1,2,3,...,10

– n, n-1, ..., 2, 1, 0, -1, -2, ..., -n

– valorile intregi din intervalul [a,b], in ordine crescatoare

– puterile lui 2 mai mici decat n

– primele n puteri ale lui 2

5 3 8

1 2 3 4 5 6 7 8 9 10

- 50 -

#include <iostream>using namespace std;

int main(){ int n,a,b,i,p; cin >> n >> a >> b; for(i=1;i<=10;i++) cout << i << " "; cout << endl; for(i=n;i>=-n;i--) cout << i << " "; cout << endl;

for(i=a;i<=b;i++) cout << i << " "; cout << endl; for(i=1;i<=n;i=2*i) cout << i << " "; cout << endl; p=1; for(i=1;i<=n;i++){ cout << p << " "; p=2*p; }}

Introducere in ANSI C++

5 4 3 2 1 0 ­1 ­2 ­3 ­4 ­5

3 4 5 6 7 8

1 2 4

1 2 4 8 16 

P7. Afisati, folosind trei metode diferite urmatoarea serie de numere pe ecran:

1 0 1 0 1 0 ..., in total n valori

n=7

1 0 1 0 1 0 1

1 0 1 0 1 0 1

1 0 1 0 1 0 1 

Cum partea de reinitializare din cadrul buclei for contine orice expresie, nu numai incrementare/decrementare, instructiunea for devine echivalenta ca functionalitate cu bucla while.

In figura 5.2 este data echivalenta dintre instructiunile for si while:

- 51 -

#include <iostream>using namespace std;

int main(){ int n,i,b; cout << "n="; cin >> n; for(i=1;i<=n/2;i++) cout << 1 << " " << 0 << " "; if (n%2==1) cout << 1; cout << endl; for(i=1;i<=n;i++) cout << i%2 << " "; cout << endl; b=1; for(i=1;i<=n;i++){ cout << b << " "; b=!b; } }

Introducere in ANSI C++

  expr1;  while (expr2){ 

instructiune;      expr3;  }

 for(expr1;expr2;expr3)

      instructiune;

Figura 5.2

Folosirea expresiei virgula reprezinta o varianta prin care putem controla mai multe variabile in cadrul aceleiasi bucle for.

P8. Afisati urmatoarele serii de numere pe ecran:

• 1 9 2 8 3 7 ... 9 1

• 1 2 4 8 ... , in total n valori

n=5

1 9 2 8 3 7 4 6 5 5 6 4 7 3 8 2 9 1

1 2 4 8 16 

Putem folosi, in cadrul instructiunii for, mai multi contori, eventual de tipuri diferite. In plus, toate partile din for sunt optionale, urmatoarea bucla fiind bucla infinita:

for(;;);

P10. Afisati tabela cu codurile ASCII, cate 10 pe un rand.

- 52 -

#include <iostream>using namespace std;

int main(){ int n,i,j,p; cout << "n="; cin >> n; for(i=1,j=9; i<10 ;i++,j--) cout << i << " " << j << " "; cout << endl; for(i=1,p=1; i<=n; i++,p*=2) cout << p << " ";}

Introducere in ANSI C++

.............

<­60    =­61    >­62    ?­63    @­64    A­65    B­66    C­67    D­68    E­69

F­70    G­71    H­72    I­73    J­74    K­75    L­76    M­77    N­78    O­79

................

5.4 break si continue

In cazul care se doreste iesirea din bucla curenta imediat, eventual in functie de o alta conditie decat cea din bucla, folosim instructiunea break. De obicei vom folosi instructiunea break pentru a nu complica conditia din bucla.

Sintaxa:

break;

Instructiunea break va cauza iesirea doar din cadrul buclei curente, in cazul in care avem mai multe bucle imbricate, iesirea facandu-se in bucla imediat superioara.

Tot pentru controlul fluxului de executie putem folosi instructiunea continue.

Sintaxa:

continue;

Analog instructiunii break, fluxul executiei din cadrul buclei curente este intrerupt, dar executie revine la conditia de oprire din bucla.

- 53 -

#include <iostream>using namespace std;

int main(){ unsigned char c; for(c=0;c<255;c++){ cout << c << "-" << int(c) << "\t"; if (c%10==9) cout << endl; }}

Introducere in ANSI C++

P11. Afisati pe ecran numerele prime mai mici decat n.

n=20

1 2 3 5 7 11 13 17 19 

P12. Afisati pe ecran urmatoarele numere, cu doua zecimale exacte:

• 1/10, 1/9, 1/8, ... 1/1, 1/-1, 1/-2, ... 1/-10

0.1 0.11 0.12 0.14 0.17 0.2 0.25 0.33 0.5 1 ­1 ­0.5 ­0.33 ­0.25 ­0.2 ­0.17 ­0.14 ­0.12 ­0.11 ­0.1 

- 54 -

#include <iostream>using namespace std;

int main(){ int i; for(i=10;i>=-10;i--){ if (i==0) continue; cout.precision(2); cout << 1./i << " "; }}

#include <iostream>using namespace std;

int main(){ int n,d,i; bool prim; cout << "n="; cin >>n; for(i=1;i<=n;i++){ prim=true; for(d=2;d<=i/2;d++) if (i%d==0){prim=false; break;} if (prim) cout << i << " "; }}

Introducere in ANSI C++

5.5 instructiunea goto

Trecerea fluxului de executie la o pozitie oarecare, identificata printr-o eticheta se poate realiza prin instructiunea goto.

Definirea etichetei se face in felul urmator:

eticheta:

iar utilizarea instructiunii goto:

goto eticheta;

Chiar daca este o instructiune pusa la dispozitie in cadrul limbajului C, folosirea acesteia nu este recomandata, folosirea instructiunilor if, if­else si while putand realiza acelasi lucru, iar depanarea este mai facila.

P13. Folosind instructiunea goto, afisati pe ecran numerele intregi din intervalul

[a,b].

3 6

3 4 5 6 

- 55 -

#include <iostream>using namespace std;

int main(){ int i,a,b; cin >> a >> b; i=a;salt: cout << i << " "; i++; if (i<=b) goto salt; // se revine la eticheta salt}

Introducere in ANSI C++

5.6 Probleme tip

In acest subcapitol vom prezenta o serie de algoritmi ce vor fi rezolvati folosind instruciuni repetitive. Vom prezenta o serie de "scheme generale" de rezolvari de algoritmi. Aceste scheme sunt reguli de forma: "daca o problema suna asa, rezolvarea este, de obicei, cam asa".

5.6.1 Probleme pe cifrele unui numar

Acest gen de probleme prelucreaza numerele intregi la nivel ce cifre componente. Accesul la cifrele unui numar este dat de Figura 5.3

Figura 5.3

De aici obtinem urmatoarele:

Si mai departe, parcurgerea unui numar intreg pozitiv, cifra cu cifra:

unde foloseste(c) este o secventa de instructiuni dependenta de enuntul problemei.

- 56 -

1234 101230 123

4

n

n%10 n/10

c= n% 10 – calculul ultimei cifre

n=n/10 – eliminarea ultimei cifre

while (n>0){ // cat timp mai avem cifrec= n% 10; foloseste(c);n=n/10;

}

Introducere in ANSI C++

O alta varianta este cea folosind instructiunea for:

P14. Afisati cifrele unui numar, cate o cifra pe un rand.

n=1942

2

4

9

1

Ultima instructiune for (cea comentata) rezolva acceasi problema intr-o varianta compacta.

- 57 -

for(;n>0;n=n/10) foloseste(n%10);

#include <iostream>using namespace std;

int main(){ int n,c; cout << "n="; cin >> n; while(n>0){ c=n%10; cout << c << endl;; n=n/10; } // for(;n;n/=10) cout << n%10 << endl;}

Introducere in ANSI C++

5.6.2 Probleme de divizibilitate

Un numar n este divizibil cu numar d daca restul impartirii lui n la d este zero, deci:

Plecand de la aceasta conditie, parcurgerea divizorilor unui numar intreg pozitiv n:

P15. Afisati divizorii unui numar intreg pozitiv citit de la tastatura.

n=120

1 2 3 4 5 6 8 10 12 15 20 24 30 40 60 120

P16. Se citeste de la tastatura un numar intreg pozitiv.

• Verificati daca este prim

• descompuneti-l in factori primi

- 58 -

n%d==0  ­ verificarea daca d|n

for(d=1;d<=n;d++) // dintre toti divizorii posibili

if (n%d==0) foloseste(d); // selectam divizorii

#include <iostream>using namespace std;

int main(){ int n,d; cout << "n="; cin >> n; for(d=1;d<=n;d++) if (n%d==0) cout << d << " ";}

Introducere in ANSI C++

n=120

120 nu este prim

2 2 2 3 5

P17. Se citesc doua numere intregi pozitive de la tastatura. Calculati

cmmdc(a,b) si cmmmc(a,b).

- 59 -

#include <iostream>using namespace std;

int main(){ int n,d; bool prim; cout << "n="; cin >> n; prim=true; for(d=2;d<=n/2;d++) if (n%d==0){ prim=false; break; } if (prim) cout << n << " este prim\n"; else cout << n << " nu este prim\n"; d=2; while(n>1) if (n%d==0){ cout << d << " "; n=n/d; } else d++;}

Introducere in ANSI C++

24 36

cmmdc(24,36)=12

cmmmc(24,36)=72

5.6.3 Sume, produse, numarari

Avem urmatoarele scheme generale:

Suma:

Produsul:

- 60 -

S=0Pentru toate valorile X de insumat

S=S+X

P=1Pentru toate valorile X de inmultit

P=P*X

#include <iostream>using namespace std;

int main(){ int a,b,cmmdc,cmmmc,r,aa,bb; cin >> a >> b; aa=a; bb=b; while(bb!=0){ r=aa%bb; aa=bb; bb=r; } cmmdc=aa; cmmmc=(a*b)/cmmdc;

cout << "cmmdc(" <<a <<"," << b <<")=" << cmmdc << endl; cout << "cmmmc(" <<a <<"," << b <<")=" << cmmmc;}

Introducere in ANSI C++

Numarari (contorizari):

P18. Se citesc doua numere intregi a si b (a<b). Calculati:

• Suma numerelor pare din interval

• a!

• S=1/a+1/(a+1)+ ... 1/b (in cazul impartirii la 0 termenul nu apare)

4 11

60 24 1.18654

P19. Se citeste un numar intreg pozitiv de la tastatura. Calculati:

• Suma cifrelor numarului

• Produsul cifrelor pare

• Prima cifra a numarului

- 61 -

ct=0Pentru toate valorile X de parcurs

if (conditie(X)) ct++

#include <iostream>using namespace std;

int main(){ int a,b,Sm,fact,i; float S; cin >> a >> b; for(Sm=0,i=a; i<=b ;i++) Sm+=i; for(fact=1,i=1; i<=a; i++) fact*=i; for(S=0,i=a; i<=b; i++) S+=1./i; cout << Sm << " " << fact << " " << S;}

Introducere in ANSI C++

• De cate ori apare prima cifra in numar

12141

9 8 1 3

P20. Se citeste un numar intreg pozitiv n de la tastatura. Calculati:

• Suma divizorilor numarului

• Cate numere prime mai mici ca n exista

- 62 -

#include <iostream>using namespace std;

int main(){ int n,nn,S,P,pc,ct,c; cin >> n; nn=n; for(S=0,P=1; n ;n/=10){ c=n%10; S+=c; if (c%2==0) P*=c; } for(n=nn;n>=10;n/=10); pc=n; for(n=nn,ct=0; n ;n/=10) if (n%10 == pc) ct++; cout << S << " " << P << " " << pc << " " << ct; }

Introducere in ANSI C++

n=10

18 4

5.6.4 Maxime si minime

Calcularea valorile minime sau maxime dintr-o serie de valori reprezinta o problema des intalnita in practica.Avem:

Maxime:

Minime:

- 63 -

max = ­infinitPentru toate valorile X de parcurs

if (X>max) max=X

min = infinitPentru toate valorile X de parcurs

if (X<min) min=X

#include <iostream>using namespace std;

int main(){ int n,d,S,ct,i; bool prim; cout << "n="; cin >> n; S=0; for(d=1;d<=n;d++) if (n%d==0) S+=d; cout << S << " "; ct=0; for(i=2;i<n;i++){ prim=true; for(d=2;d<=i/2;d++) if (i%d==0) prim=false; if (prim) ct++; } cout << ct;}

Introducere in ANSI C++

In aceste scheme, infinit reprezinta valoarea minima pe care o poate lua X si depinde de tipul de date cu care lucram.

Biblioteca values.h ofera in acest sens o serie de constante predefinite utile, cum ar fi MAXINT, -MAXINT, MAXFLOAT etc.

P21. Se citeste un numar intreg pozitiv de la tastatura. Calculati:

• Cifra maxima a numarului

• De cate ori apara cifra minima

25221

12 8 2 3

5.6.5 Citirea mai multor numere de la tastatura

In cazul in care dorim prelucrarea a mai multe numere citite, avem urmatoarele rezolvari:

a. Utilizatorul, ca prima faza, introduce numarul de valori ce vor fi citite ulterior:

- 64 -

#include <iostream>using namespace std;

int main(){ int n,nn,S,P,pc,ct,c; cin >> n; nn=n; for(S=0,P=1; n ;n/=10){ c=n%10; S+=c; if (c%2==0) P*=c; } for(n=nn;n>=10;n/=10); pc=n; for(n=nn,ct=0; n ;n/=10) if (n%10 == pc) ct++; cout << S << " " << P << " " << pc << " " << ct; }

Introducere in ANSI C++

b. Se cunoaste o conditie cat timp se vor citi valori:

P22. Se citeste un numar n, apoi n valori reale de la tastatura. Calculati:

• maximul

• produsul

5

1 1 5 2 1

5 10

- 65 -

cin >> n; // numarul de valori de cititfor(i=1;i<=n;i++){ // de n ori

cin >> X; // citim numarulfoloseste(X); // il utilizam conform enuntului

}

cin >> X; // citim valoarewhile (cond(X)){ // cat timp conditia e respectata

foloseste(X); // il utilizam conform enuntuluicin >> X; // citim numarul

}

#include <iostream>#include <values.h>using namespace std;

int main(){ int n,x,i,max,P; max=-MAXINT; P=1; cin >> n; for(i=1;i<=n;i++){ cin >> x; P=P*x; if (x>max) max=x; } cout << max << " " << P;}

Introducere in ANSI C++

P23. Se citesc numere intregi pana la intalnirea valorii 0. Calculati:

• Suma valorilor pare

• Minimul si de cate ori apare acesta

2 3 1 4 6 1 1 1 6 1 12 0

30

1 apare de 5 ori

5.6.6 Generarea unor serii complexe de numere

P24. Se citeste n (intreg pozitiv). Afisati pe ecran:

a: 1 1 2 1 2 3 ... 1 2...n

b: 1 2 2 3 3 3 ... n n ...n

- 66 -

#include <iostream>#include <values.h>using namespace std;

int main(){ int x,i,min,ct_min,S; min=MAXINT; ct_min=0; S=0; cin >> x; while (x!=0){ if (x%2==0) S=S+x; if (x==min) ct_min++; else if (x<min){ min=x; ct_min=1; } cin >> x; } cout << S << endl; cout << min << " apare de " << ct_min << " ori"; }

Introducere in ANSI C++

5

1  1 2  1 2 3  1 2 3 4  1 2 3 4 5

1  2 2  3 3 3  4 4 4 4  5 5 5 5 5 

P25. Se citeste n (intreg pozitiv). Afisati pe ecran, sub forma data:

1 1 2 1 1 2 3 2 1....................1 ................ 1

1:1 2:1,2 .... 6: 1,2,3,6 – numarul urmat de divizori......

1 2 3 4 52 3 4 5 63 4 5 6 74 5 6 7 85 6 7 8 9 (n=5)

- 67 -

#include <iostream>using namespace std;

int main(){ int n,i,k; cin >>n; for(k=1;k<=n;k++){ for(i=1;i<=k;i++) cout << i << " "; cout << " "; } cout << endl; for(k=1;k<=n;k++){ for(i=1;i<=k;i++) cout << k << " "; cout << " "; }}

Introducere in ANSI C++

4

      1

    1 2 1

  1 2 3 2 1

1 2 3 4 3 2 1

­­­­­­­­­­­­­

1: 1

2: 1,2

3: 1,3

4: 1,2,4

­­­­­­­­­­­­­

2 3 4 5

3 4 5 6

4 5 6 7

5 6 7 8

- 68 -

#include <iostream>using namespace std;

int main(){ int n,i,k,d; cin >>n; for(k=1;k<=n;k++){ for(i=1;i<=n-k;i++) cout << " "; for(i=1;i<=k;i++) cout << i << " "; for(i=k-1;i>=1;i--) cout << i << " "; cout << endl; } cout << "-------------" <<endl; for(k=1;k<=n;k++){ cout << k << ": "; for(d=1;d<=k/2;d++) if (k%d==0) cout << d << ","; cout << k << endl; } cout << "-------------" << endl; for(k=1;k<=n;k++){ for(i=1;i<=n;i++) cout << i+k << " "; cout << endl; }}

Introducere in ANSI C++

5.7 Probleme propuse

Pp1. Refaceti solutiile propuse ale problemelor rezolvate in acest capitol, astfel incat sa folositi alt tip de instructiune repetitiva fata de cea utilizata in rezolvarea initiala (de exemplu in loc de for folositi while).

Pp2. Se citeste numarul intreg pozitiv n de la tastatura. Calculati valoarea urmatoarele expresii, folosind pentru fiecare expresie un alt tip de instructiune repetitiva:

E = n + n/2 + n/3 + ... + 1

P = 1! + 2! + ... +n!

S = 12 + 42 + 72 + ... + (3n-2)2

Pp3. Se citesc doua numere intregi de la tastatura. Determinati daca cele doua numere sunt formate din aceleasi cifre, indiferent de cate ori apar. De exemplu 123 si 11321 sunt formate din aceleasi cifre.

Pp4. Calculati ultima cifra a expresiei E= ab, a si b valori intregi pozitive citite de la tastatura.

Pp5. Se citesc numere intregi de la tastatura pana la intalnirea unui numar negativ. Afisati numarul citit care are cei mai multi divizori.

Pp6. Se citeste de la tastatura un numar intreg n. Afisati pe ecran urmatoarele serii de numere, pastrand formatul:

1 2 1 2 3 2 1 2 3....................n ......1....... n

1:1 2:2,2 3:3,3,3...... n:n,n,n,...,n

1 0 1 0 10 1 0 1 01 0 1 0 10 1 0 1 01 0 1 0 1 (n=5)

Pp7. Afisati pe ecran tabla inmultirii pana la 10.

Pp8. Se citeste n intreg, apoi n numere intregi de la tastatura. Afisati:

– cel mai mare si cel mai mic numar prim

– suma numerelor care au cifrele in ordine crescatoare

- 69 -

Introducere in ANSI C++

Capitolul 6 – Vectori (matrici)

In momentul in care complexitatea programelor creste, devine evidenta necesitatea folosirii unor tipuri de date complexe. Folosirea variabilelor simple permite stocarea unei singure valori, iar acest lucru devine un impediment major in momentul in care, sa zicem, avem nevoie de stocarea si realizarea de diferite operatii pe 100 de numere intregi.

Un vector (tablou unidimensiunal) este o structura de date care poate stoca mai multe valori de acelasi tip.

6.1 Declararea si initializarea

Exact ca si in cazul variabilelor simple, daca dorim utilizarea unui vector acesta trebuie sa fie declarat. C++ ofera mai multe moduri de a declara/initializa un vector, sub forma urmatoare:

tip nume[dimensiune] sau

tip nume[dimensiune]={v1,...,vn}, unde vi au un tip convertibil la tip sau

tip nume[]={v1,v2,...,vn}, unde vi au un tip convertibil la tip

P1. Exemple de declarari/initializari de vectori si cat ocupa acestia in memorie.

Vectorul A ocupa 400 octeti

Vectorul B ocupa 40 octeti

- 70 -

#include <iostream>using namespace std;

int main(){ int A[100]; // vector de intregi neinitilalizat int B[10]={1,2,3,4};// vector de intregi partial initializat float X[]={3.14, 5, 2.5};// vector de reali complet initializat cout << "Vectorul A ocupa " << sizeof(A) << " octeti"; cout << "Vectorul B ocupa " << sizeof(B) << " octeti"; cout << "Vectorul X ocupa " << sizeof(X) << " octeti"; cout << "Vectorul X are " << sizeof(X)/sizeof(int)<< " valori";

}

Introducere in ANSI C++

Vectorul X ocupa 12 octeti

Vectorul X are 3 valori

Observam ca specificarea dimensiunii va duce la alocarea unui spatiu de memorie suficient pentru a pastra toate valorile din vector, iar in cazul in care nu avem specificata dimensiunea aceasta este data de numarul de valori cu care initializam vectorul.

Accesul la elementele unui vector se face folosind indici. Fie urmatorea declaratie de vector:

int A[10]={1,2,3,4};

In acest caz se aloca in memorie un numar de 10 intregi, primele patru valori fiind initializate. In memorie situatia va asemanatoare cu cea descrisa in figura 6.1:

Figura 6.1

Observam ca primul element este A[0], al doilea A[1], si, in final, ultimul element valid va fi A[9]. Primele patru elemente, cele initializate, au valori cunoscute, in rest nu stim ce valori exista in vector. In functie de compilator, s-ar putea ca ele sa fie initializate cu valoare 0, dar este mai bine sa nu contam pe acest fapt.

Daca, in continuare, avem urmatoarele atribuiri:

A[0]=5;

A[9]=A[1]+A[2]+A[3];

vom avea urmatoarea situatie (Figura 6.2):

Figura 6.2

- 71 -

1 2 3 4 ? ?...

A[0] A[1] A[9]

5 2 3 4 ? 9...

A[0] A[1] A[9]

Introducere in ANSI C++

6.2 Citirea si scrierea vectorilor

Vom prezenta in continuare primele operatii pe vectori.

In cazul in care dorim sa realizam citirea si afisarea unui vector, exista posibilitatea de a accesa vectorul elemente cu element prin intermediul unor indici constanti, asa cum se observa in problema urmatoare:

P2. Se citesc cinci valori ale unui vectori de intregi. Afisati vectorul in ordine

inversa.

Dati elementele vectorului A:2 4 1 3 5

A: 5 3 1 4 2

Aceasta abordare nu acopera urmatoarele cazuri:

- daca avem un numar mare de elemente in vector

- daca numarul de elemente din vector difera de la rulare la rulare

Pentru a rezolva aceste situatii, in loc sa accesam valorile din vector folosind indici constanti, vom folosi o variabila ca indice. Accesul la elementele vectorului se va face folosind o notatie de forma A[i], unde i va fi o valoare intrega ce reprezinta un indice valid in vector, adica o valoare din multimea {0,1,...,n-1}.

Citirea unui element va fi de forma:

cin >> A[i];

unde i va parcurge indicii vectorului.

- 72 -

#include <iostream>using namespace std;

int main(){ int A[5]; cout << "Dati elementele vectorului A:"; cin >> A[0] >> A[1] >> A[2] >> A[3] >> A[4]; cout << "A: " << A[4] << " " << A[3] << " "; cout << A[2] << " " << A[1] << " " << A[0]; }

Introducere in ANSI C++

P3. Se citesc cinci valori ale unui vectori de intregi. Afisati vectorul in ordine

inversa. (varianta 2)

Dati elementele vectorului A:3 4 5 6 7

A: 7 6 5 4 3

In general, pentru a creste flexibilitatea programului, vom da posibilitatea utilizatorului sa foloseasca vectori de lungime variabila, lungime limitata doar de modul in care am declarat vectorul. De acum inainte, atunci cand ne referim la un vector, ne vom referi la o pereche de forma (A,n), unde A este vectorul, iar n este lungimea actuala a acestuia.

P4. Se citeste un vector de la tastatura. Afisati vectorul pe ecran.

4

3 4 1 2

3 4 1 2

- 73 -

#include <iostream>using namespace std;

int main(){ int A[5]; int i; cout << "Dati elementele vectorului A:"; for(i=0;i<5;i++) cin >> A[i]; cout << "A: "; for(i=4;i>=0;i--) cout << A[i] << " "; }

#include <iostream>using namespace std;

int main(){ int A[100],n,i;// vectorul si lungimea curenta (0<= n <=100) cin >>n; // citim lungimea vectorului for(i=0;i<n;i++) cin >> A[i]; // apoi vectorul, element cu element for(i=0;i<n;i++) cout << A[i] << " "; // afisarea, element cu element }

Introducere in ANSI C++

Aspectul „sec” al aplicatiei este remediat in exemplul urmator:

P5. Se citeste un vector de la tastatura. Afisati vectorul pe ecran. (varianta

cosmetizata)

n=3

A[0]=7

A[1]=15

A[2]=9

A=(7,15,9)

In general recomand folosirea variantei simple daca lucram programul „pe foaie”, orice varianta practica, pe calculator, folosind o varianta de citire/scriere user-friendly.

Dupa cum observam in ultimele exemple, accesul la elementele vectorului (A,n) respecta, in general, urmatoarea schema generala:

Modul in care folosim elementele vectorului depinde de la problema la problema.

- 74 -

#include <iostream>using namespace std;

int main(){ int A[100],n; int i; cout << "n="; cin >>n; for(i=0;i<n;i++){ cout << "A[" << i << "]="; cin >> A[i]; } cout << "A=("; for(i=0;i<n-1;i++) cout << A[i] << ","; cout << A[n-1] << ")"; }

for(i=0;i<n;i++)foloseste(A[i]);

Introducere in ANSI C++

6.3 Generari de vectori

Uneori elementele vectorilor nu vor fi valori citite de la tastatura ci valori construite in functie de enunt. Urmatoarele exemple ilustreaza cateva cazuri mai des intalnite in practica.

In problema urmatoare exploatam relatia dintre indicele si valoarea memorata pe pozitia respectiva:

P6. Se citeste de la tastatura un numar intreg 1<=n<=100. Generati si afisati

pe ecran urmatorii vectori de lungime n.

A: 1,2,3,4,...

B: 0,2,0,4,0,6,...

C: n-1,n-2, ..., 2,1,0

D: 0,1,0,1,0,...

n=10

A:1 2 3 4 5 6 7 8 9 10

B:0 1 0 3 0 5 0 7 0 9

C:9 8 7 6 5 4 3 2 1 0

D:0 1 0 1 0 1 0 1 0 1 

- 75 -

#include <iostream>using namespace std;

int main(){ int A[100],B[100],C[100],D[100],n; int i; cout << "n="; cin >>n; for(i=0;i<n;i++) A[i]=i+1; for(i=0;i<n;i++) B[i]= (i%2==0)? 0:i; for(i=0;i<n;i++) C[i]=n-1-i; for(i=0;i<n;i++) D[i]=i%2; cout << "A:"; for(i=0;i<n;i++) cout << A[i] << " "; cout << "\nB:"; for(i=0;i<n;i++) cout << B[i] << " "; cout << "\nC:"; for(i=0;i<n;i++) cout << C[i] << " "; cout << "\nD:"; for(i=0;i<n;i++) cout << D[i] << " ";}

Introducere in ANSI C++

Generarea recursiva a vectorilor este o varianta intuitiva de rezolvare, daca enuntul problemei permite folosirea unei relatii prin care construim un element in functie de cele deja generate.

P7. Se citeste de la tastatura un numar intreg 1<=n<=20. Generati recursiv,

apoi afisati pe ecran urmatorii vectori de lungime n.

A: 1,2,3,4,...

B: 1,1,2,3,5,8,...

C: 1,2,4,8,16,...

D: 0,1,0,1,0,...

n=10

A:1 2 3 4 5 6 7 8 9 10

B:1 1 2 3 5 8 13 21 34 55

C:1 2 4 8 16 32 64 128 256 512

D:0 1 0 1 0 1 0 1 0 1 

- 76 -

#include <iostream>using namespace std;

int main(){ int A[20],B[20],D[20],n,i; long C[20]; cout << "n="; cin >>n; A[0]=1; for(i=1;i<n;i++) A[i]=A[i-1]+1; B[0]=B[1]=1; for(i=2;i<n;i++) B[i]= B[i-1]+B[i-2]; C[0]=1; for(i=1;i<n;i++) C[i]=2*C[i-1]; D[0]=0; for(i=1;i<n;i++) D[i]=!D[i-1]; cout << "A:"; for(i=0;i<n;i++) cout << A[i] << " "; cout << "\nB:"; for(i=0;i<n;i++) cout << B[i] << " "; cout << "\nC:"; for(i=0;i<n;i++) cout << C[i] << " "; cout << "\nD:"; for(i=0;i<n;i++) cout << D[i] << " ";}

Introducere in ANSI C++

Generarea de valori aleatoare in vectori poate fi utila daca dorim testarea unor algoritmi pe vectori si dorim sa evitam introducerea de valori de la tastatura.

P8. Generati un vector de lungime citita de la tastatura, continand valori

aleatoare in intervalul [a,b], a<b, numere pozitive.

n=10

a=30

b=40

A:36 40 36 32 31 34 30 36 33 31

Functia rand(), definita intr-o biblioteca standard ANSI C++, stdlib, va returna un numar de tip long int pseudo-aleator. Operatia modulo va face ca valoarea aleatoare „sa cada” in intervalul ales de noi, in cazul acesta [0,b-a]. Adunand la aceasta valoare a, vom obtine valoarea aleatoare in intervalul dorit.

6.4 Cateva probleme simple

Vom da mai departe cateva probleme simple cu vectori, probleme intalnite des in practica.

P9. Se citeste un vector de la tastatura. Afisati:

a. suma valorilor din vector

b. produsul elementelor pare din vector

c. maximul valorilor din vector

- 77 -

#include <iostream>using namespace std;

int main(){ int A[100],n; int i,a,b; cout << "n="; cin >>n; cout << "a="; cin >>a; cout << "b="; cin >>b; for(i=0;i<n;i++) A[i]=a + rand()%(b-a+1); cout << "A:"; for(i=0;i<n;i++) cout << A[i] << " ";}

Introducere in ANSI C++

d. minimul valorilor pozitive

n=6

1 ­1 2 ­2 3 ­3

Suma: 0

Produsul valorilor pare: ­4

Maximul: 3

minimul valorilor pozitive: 1

P10. Generati un vector aleator de 2n elemente din [1,10], n citit de la tastatura,

n<10. Calculati si afisati pe ecran:

E=A[0]/A[1]+A[2]/A[3]+ ... +A[2n-2]/A[2n-1]

F=max(A[0]) + max(A[0],A[1]) + ...max(A[0],A[1],...,A[2n-1])

- 78 -

#include <iostream>using namespace std;

int main(){ int A[100],n,i; int S,P; cout << "n="; cin >>n; for(i=0;i<n;i++) cin >> A[i]; S=0;P=1; for(i=0;i<n;i++){ S=S+A[i]; if (A[i]%2==0) P=P*A[i]; } cout << "Suma: " << S; cout << "\nProdusul valorilor pare: " << P; }

Introducere in ANSI C++

n=2

Vectorul:4 7 8 6

E: 1.90476

F: 27

P11. Verificati daca un vector citit de la tastatura este sau nu:

a. sortat crescator

b. este sir Fibonacci (primele doua vaori sunt 1, in rest fiecare element de

obtine insumand cele doua valori anterioare)

c. simetric

- 79 -

#include <iostream>#include <values.h>using namespace std;

int main(){ int A[20],n,i; float E; int F,max; cout << "n="; cin >>n; for(i=0;i<2*n;i++) A[i]=1+rand()%10; E=0; for(i=0;i<2*n-1;i+=2) E = E + float(A[i])/A[i+1]; max=-MAXINT; F=0; for(i=0;i<2*n;i++){ if(A[i]>max) max=A[i]; F=F+max; } cout << "Vectorul:"; for(i=0;i<2*n;i++) cout << A[i] << " "; cout << "\nE: " << E; cout << "\nF: " << F; }

Introducere in ANSI C++

n=5

1 1 2 3 5

Vectorul dat este crescator, este Fibonacci, nu este simetric.

sau:

n=6

1 6 2 2 6 1

Vectorul dat nu este crescator, nu este Fibonacci, este simetric.

- 80 -

#include <iostream>using namespace std;

int main(){ int A[100],n,i,j; bool cresc, fibo, sim; cout << "n="; cin >>n; for(i=0;i<n;i++) cin >> A[i]; cresc=true; for(i=0;i<n-1;i++) if (A[i]>A[i+1]) cresc=false; if ((A[0]==1)&&(A[1]==1)) fibo=true; for(i=2;fibo && i<n-1;i++) if (A[i]!=A[i-1]+A[i-2]) fibo=false; sim=true; for(i=0,j=n-1;sim && i<j;i++,j--) if (A[i]!=A[j]) sim=false; cout << "Vectorul dat "; if (!cresc) cout << "nu "; cout << "este crescator, "; if (!fibo) cout << "nu "; cout << "este Fibonacci, "; if (!sim) cout << "nu "; cout << "este simetric."; }

Introducere in ANSI C++

6.5 Sortarea si interclasarea

Problema sortarii unui vector, este problema reasezarii elementelor in asa fel incat ele sa respecte o relatie de ordine (crescatoare). Problema este bine reprezentata in literatura de specialitate (vezi [1], [4], [6], [8]). Prezentam in continuare trei metode de sortare consacrate.

P12. Sortarea prin selectie

n=5

2 4 5 1 3

Vectorul sortat: 1 2 3 4 5

Ideea „din spatele”algoritmului este urmatoarea:

- aducem pe prima pozitie elementul minim din vector, prin interschimbarea lui A[0] cu acele elemente dintre A[1],A[2],...A[n-1] care sunt mai mici ca A[0].

- repetam algoritmul descris la pasul anterior pe cazul general, aducand pe pozitia A[i] minimul dintre A[i],A[i+1],...,A[n-1]

- la sfarsit toate valorile sunt pe pozitiile corecte

- 81 -

#include <iostream>using namespace std;

int main(){ int A[100],n,i,j,t; cout << "n="; cin >>n; for(i=0;i<n;i++) cin >> A[i]; for(i=0;i<n-1;i++) for(j=i+1;j<n;j++) if (A[j]<A[i]){ t=A[i]; A[i]=A[j]; A[j]=t; } cout << "Vectorul sortat: " ; for(i=0;i<n;i++) cout << A[i] << " "; }

Introducere in ANSI C++

P13. Bubble Sort

n=5

2 4 5 1 3

Vectorul sortat: 1 2 3 4 5

Ideea este urmatoarea:

- comparam, rand pe rand, toate valorile succesive din vector

- daca elementul din stanga este mai mare ca cel din dreapta, atunci interschimbam elementele

- in urma primei treceri prin vector, elementul maxim a ajuns sigur pe ultima pozitie

- repetari succesive ale algoritmului vor duce la deplasari ale elementelor mari spre sfarsit, pana la ocuparea pozitiei corecte

- cum la fiecare trecere cel putin un element ajunge la locul corect, din maxim n-1 treceri vor avea vectorul sortat crescator

- 82 -

#include <iostream>using namespace std;

int main(){ int A[100],n,i,k,t; cout << "n="; cin >>n; for(i=0;i<n;i++) cin >> A[i]; for(k=1;k<=n-1;k++) for(i=0;i<n-1;i++) if (A[i]>A[i+1]){ t=A[i]; A[i]=A[i+1]; A[i+1]=t; } cout << "Vectorul sortat: " ; for(i=0;i<n;i++) cout << A[i] << " "; }

Introducere in ANSI C++

P14. Sortarea prin numarare

Pentru a putea aplica sortarea prin numarare, se stie ca intervalul in care A[i] poare lua valori este de forma [0,m], cu m cunoscut si nu foarte mare. Pentru problema noastra am considerat m=10.

n=10

Vectorul initial: 3 6 7 5 3 5 6 2 9 1

Vectorul sortat: 1 2 3 3 5 5 6 6 7 9 

Functionarea algoritmului:

- numaram fiecare valoare de cate ori apare in vector

- odata avut vectorul de contori, nu avem decat sa completam valorile, de la cea mai mica la cea mai mare, de cate ori trebuie in vectorul rezultat

- 83 -

#include <iostream>using namespace std;

int main(){ int A[100],n,i,k,j; int ct[100]; cout << "n="; cin >>n; for(i=0;i<n;i++) A[i]=rand()%10; cout << "Vectorul initial: "; for(i=0;i<n;i++) cout << A[i] << " "; // contorizarea valorilor din vector for(i=0;i<10;i++) ct[i]=0; for(i=0;i<n;i++) ct[A[i]]++; // completarea valorilor in vector i=0; for(k=0;k<10;k++) for(j=1;j<=ct[k];j++) A[i++]=k; cout << "\nVectorul sortat: " ; for(i=0;i<n;i++) cout << A[i] << " "; }

Introducere in ANSI C++

Interclasarea se definiste ca fiind algoritmul care, primind doi vectori sortati, obtine un al treilea vector sortat continand elementele vectorilor initiali.

P15. Interclasarea vectorilor

A: 2 4 5 7 10

B: 1 4 5 7 9 12 16

C: 1 2 4 4 5 5 7 7 9 12 16 10 

Vectorii au fost generati aleator, dar in ordine crescatoare.

- 84 -

#include <iostream>using namespace std;

int main(){ int A[100],B[100],n,m,i,j; int C[100],k; n=2+rand()%10; A[0]=2; for(i=1;i<n;i++) A[i]=A[i-1]+1+rand()%3; m=2+rand()%10; B[0]=1; for(i=1;i<m;i++) B[i]=B[i-1]+1+rand()%4; cout << "A: "; for(i=0;i<n;i++) cout << A[i] << " "; cout << "\nB: ";for(i=0;i<m;i++) cout << B[i] << " "; i=j=k=0; while ((i<n)&&(j<m)) if (A[i]<A[j]) C[k++]=A[i++]; else C[k++]=B[j++]; while(i<n) C[k++]=A[i++]; while(j<m) C[k++]=B[j++]; cout << "\nC: ";for(i=0;i<m+n;i++) cout << C[i] << " "; }

Introducere in ANSI C++

6.6 Declararea matricilor

Limbajul C++ permite, pe langa matricile unidimensionale (vectorii), matrici bidimensionale si multidimensionale.

Declararea unei matrici:

tip nume[dimensiune1][dimensiune2]  sau, mai general,

tip nume[dimensiune1][dimensiune2]...[dimensiunen]

Cele mai folosite, pe langa vectori, sunt matricile bidimensionale, numite de acum, din comoditate, matrici.

Exista posibilitatea initializarii elementelor unei matrici, dand un „vector de vectori” de valori, valori ce vor fi completate in matrice pe linii.

P16. Declarari de matrici. Cantitatea de memorie ocupata de matrici.

A[10][10] ocupa 400 octeti

B[4][3] ocupa 48 octeti

6.7 Citirea si afisarea matricilor

Accesul la elementele unei matrici se face tot prin intermediul indicilor. Astfel, pentru o declaratie de forma int A[10][10] indicii corespunzatori sunt dati in Figura 6.3.

- 85 -

#include <iostream>using namespace std;

int main(){ int A[10][10]; float B[4][3]={{1,2}, {3,4}, {5,6}}; cout << "A[10][10] ocupa " << sizeof(A) << " octeti"; cout << "\nB[4][3] ocupa " << sizeof(B) << " octeti"; }

Introducere in ANSI C++

Figura 6.3

P17. Se citeste o matrice de la tastatura. Afisati matricea.

n=2

m=2

A[0][0]=1

A[0][1]=2

- 86 -

A[0][0] A[0][1] A[0][2]

A[1][0] A[1][1] A[1][2]

. . .

. . .

A[0][9]

A[1][9]

A[9][0] A[9][1] A[9][2] . . . A[9][9]

. . .

. . .

. . .

. . .

Linia 0

Linia 1

Linia 9C

oloana 1

Coloana 0

Coloana 9

#include <iostream>using namespace std;

int main(){ int A[10][10],n,m,i,j; cout << "n=";cin >>n; cout << "m=";cin >>m; for(i=0;i<n;i++) for(j=0;j<n;j++){ cout << "A[" << i<< "][" << j << "]="; cin >> A[i][j]; } for(i=0;i<n;i++){ for(j=0;j<n;j++) cout << A[i][j] << " "; cout << endl;

} }

Introducere in ANSI C++

A[1][0]=3

A[1][1]=4

1 2

3 4

Analog citirii vectorilor, citirea efectiva a elementelor din matrice este precedata de specificarea numarului de linii si de coloane. Primul indice reprezinta linia, iar al doilea coloana.

In general, parcurgerea pe linii a unei matrici are loc astfel:

6.8 Probleme cu matrici

P18. Afisati pe coloane o matrice initializata din cod

1 4 7

2 5 8

3 6 9

- 87 -

for(i=0;i<n;i++)for(j=0;j<m;j++)

foloseste(A[i][j]);

#include <iostream>using namespace std;

int main(){ int A[10][10]={ {1,2,3}, {4,5,6}, {7,8,9}}; int n=3,m=3,i,j; for(j=0;j<n;j++){ for(i=0;i<n;i++) cout<<A[i][j]<<" "; cout << endl;

}}

Introducere in ANSI C++

P19. Generati si afisati urmatoarele matrici:

1   1   1   1 1   0   1   0 1   2   3   4  

2   2   2   2 0   1   0   1 5   6   7   8  

3   3   3   3 1   0   1   0 9   10  11  12  

4   4   4   4 0   1   0   1 13  14  15  16  

- 88 -

#include <iostream>using namespace std;

int main(){ int A[10][10], B[10][10], C[10][10]; int n,m,i,j,v;

cout << "n="; cin >> n; cout << "m="; cin >> m; v=1; for(i=0;i<n;i++) for(j=0;j<n;j++){ A[i][j]=i+1; B[i][j]=(i+j+1)%2; C[i][j]=v++; } for(i=0;i<n;i++){ for(j=0;j<n;j++) cout << A[i][j] << " "; cout << endl; }

for(i=0;i<n;i++){ for(j=0;j<n;j++) cout << B[i][j] << " "; cout << endl; }

for(i=0;i<n;i++){ for(j=0;j<n;j++) cout << C[i][j] << " "; cout << endl; }}

Introducere in ANSI C++

In cazul in care numarul de linii si cel de coloane este identic spunem despre matrice ca este patratica. In acest caz, vorbim despre diagonala principala si cea secundara. Relatia dintre indici raportata la diagonale este data in Figura 6.4:

Figura 6.4

P20. Completati o matrice sub forma:

- 89 -

1 2 2 2 2 15 1 2 2 1 35 5 1 1 3 35 5 1 1 3 35 1 4 4 1 31 4 4 4 4 1

i==ji<j

i>j

i+j==

n-1

i+j<n-1

i+j>n-1

#include <iostream>using namespace std;

int main(){ int A[10][10], n,i,j; cout << "n="; cin >> n;

for(i=0;i<n;i++) for(j=0;j<n;j++){ if (i<j) if (i+j<n-1) A[i][j]=2; else A[i][j]=3; else if (i+j<n-1) A[i][j]=5; else A[i][j]=4; if ((i==j)||(i+j==n-1)) A[i][j]=1; } for(i=0;i<n;i++){ for(j=0;j<n;j++) cout << A[i][j] << " "; cout << endl; }}

Introducere in ANSI C++

P21. O matrice contine cifre pe primele n-1 linii. Considerand fiecare rand al

matricii ca fiind numarul in baza 10 corespunzator, completati pe ultima linie

suma numerelor zecimale de pe liniile date.

0 2 4 5

0 0 9 1

1 2 3 4

1 5 7 0

- 90 -

#include <iostream>using namespace std;

int main(){ int A[10][10], n,i,j,t; cout << "n="; cin >> n; for(i=0;i<n-1;i++) for(j=0;j<n;j++) cin >> A[i][j]; t=0; for(j=n-1;j>=0;j--){ for(i=0;i<n-1;i++) t+=A[i][j]; A[n-1][j]=t%10; t=t/10; } for(i=0;i<n;i++){ for(j=0;j<n;j++) cout << A[i][j] << " "; cout << endl; }}

Introducere in ANSI C++

6.9 Probleme propuse

Pp1. Generati si afisati vectorii (n intreg citit de la tastatura):

a: 1 0 2 0 3 0 ...

b: 1 2 3 5 7 11 ... (primele n numere prime)

c: 1 1 1 3 5 9 17 ... (tribonacci :))

Pp2. Se citeste un vector de la tastatura. Calculati:

S=a[0]*a[1]+a[1]*a[2] + ... + a[n-2]*a[n-1]

E = suma valorilor cu indice prim

F = produsul ultimelor cifre

Pp3. Se citesc doi vectori de intregi de la tastatura. Determinati daca cei doi vectori sunt formate din aceleasi valori, indiferent de ordine si cate ori apar. De exemplu a: 1,2,3 si b:1,1,3,2,1 satisfac proprietatea ceruta.

Pp4. Un vector contine cifre. Care este cel mai mare numar de trei cifre care poate fi format din cifrele vectorului?

Pp5. Generati un vector continand n valori intregi in intervalul [-100,100]. Mutati valorile pozitive la incepu si cele negative la sfarsitul vectorului (dintr-o singura parcurgere).

Pp6. Se citeste o matrice de la tastaura. Verificati daca elementele de pe diagonale sunt in ordine crescatoare sau descrescatoare, afisand mesaje sugestive pe ecran.

Pp7. Se citesc o matrice patratica de nxn si un vector de lungime n. Verificati de cate ori regasim vectorul in matrice, ca linie sau coloana.

Pp8. O matrice contine valori in intervalul [1,100]. Generati un vector continand valori din [1,100] care NU se regasesc in matrice, in ordine descrescatoare.

- 91 -

Introducere in ANSI C++

Capitolul 7 – Siruri de caractere

Datorita gradului de utilizare, acestui tip structurat de date i s-a acordat o atentie deosebita; exista functii de citire/scriere specifice, plus o serie functii de uz general pe siruri de caractere definite in biblioteca standard.

7.1 Declaratie si initializare

Forma generala a declaratiei unui sir de caractere este:

char nume[dimensiune] sau

char nume[dimensiune]=”c1c2...cn”, sau

char nume[]=”c1c2...cn”

Urmatorul stil de initializare nu este recomandat, desi este suportat de catre compilator:

char nume[dimensiune]={c1,c2,...,cn}

P1. Exemple de declarari/initializari de siruri.

Vectorul S ocupa 100 octeti

Vectorul s ocupa 20 octeti

Vectorul X ocupa 4 octeti

Vectorul Y ocupa 4 octeti

- 92 -

#include <iostream>using namespace std;

int main(){ char S[100]; // sir neinitilalizat char s[20]="Ana are laptop";// sir initializat char X[]="abc"; // initializare corecta char Y[]={'x','y','z'}; // initializare nerecomandata cout << "Vectorul S ocupa " << sizeof(S) << " octeti\n"; cout << "Vectorul s ocupa " << sizeof(s) << " octeti\n"; cout << "Vectorul X ocupa " << sizeof(X) << " octeti\n"; cout << "Vectorul Y ocupa " << sizeof(X) << " octeti"; }

Introducere in ANSI C++

Vom exemplifica in constinuare modul de memorare a sirurilor de caractere in C. In exemplul anterior observam ca, in urma declaratiei

char X[]=”abc”;

in memorie X ocupa 4 octeti. Cum unui caracter ii este necesar un octet pentru a fi memorat, observam ca s-a alocat un octet in plus. Motivul este cel ca in limbajul C este folosit terminatorul NULL pentru a marca sfarsitul unui sir de caractere. Pentru exemplul de mai sus avem urmatoarea situatia dinfigura 7.1:

Figura 7.1

Acelasi lucru se intampla atunci cand folosi constante de tip sir de caractere, compilatorul adaugand automat terminatia NULL. Caracterul ‘\0’ este caracterul cu codul ASCII egal cu zero si este diferit de caracterul ‘0’.

In general, in cazul in care declaram o variabila de tip sir de caractere, trebuie tinut cont de terminatorul NULL, deci practic trebuie sa declaram cu dimensiune cu 1 mai mare decat a sirului efectiv.

Diferenta de reprezentare dintre un caracter si sirul de caractere identic este evidentiat de exemplul urmator.

P2. Diferenta dintre un caracter si sirul de caractere corespunzator.

Caracterul 'A' ocupa 1 octeti

Sirul "A" ocupa 2 octeti

Este necesara folosirea secventelor escape pentru afisarea ‘ si ”.

- 93 -

‘ a’ ‘ b’ ‘ c’ ‘ \0’ ? ...

X[0] X[1]

#include <iostream>using namespace std;

int main(){ cout << "Caracterul \'A\' ocupa " << sizeof('A') << " octeti\n"; cout << "Sirul \"A\" ocupa " << sizeof("A") << " octeti\n"; }

Introducere in ANSI C++

Diferenta de reprezentare este evidentiata de Figura 7.2:

Figura 7.2

7.2 Operatii de citire/scriere pe siruri de caractere

Citirea si scrierea sirurilor de caractere este mult simplificata fata de citirea vectorilor, nu mai este necesara memorarea lungimii actuale (datorita terminatorului NULL) si nu trebuie sa citim caracter cu caracter. Urmatorul exemplu da diferite moduri de citire/scriere:

P3. Citirea/scierea sirurilor de caractere.

Introduceti un sir: Ana are laptop

Introduceti inca un sir: Ana are laptop

Primul sir introdus: Ana are laptop

Al doilea sir introdus: Ana

- 94 -

‘ A’ ‘ A’ ‘ \0’

Caracterul ‘ A’ Sirul de caractere “A”

#include <iostream>using namespace std;

int main(){ char S1[100],S2[100]; cout << "Introduceti un sir: "; cin.getline(S1,100); // citeste inclusiv spatiile cout << "Introduceti inca un sir: "; cin >> S2; // citeste pana la primul caracter "alb" cout << "Primul sir introdus: " << S1 << "\n"; cout << "Al doilea sir introdus: " << S2; }

Introducere in ANSI C++

Observam ca citirea nu trebuie facuta caracter cu caracter. Daca S este un sir de caractere, folosind o constructie de forma

cin >> S; 

citirea se opreste la primul caracter „alb” (spatiu, tab, enter).

Citirea intregului rand, adica pana la apasarea tastei Enter se face folosind:

cin.getline(S,dimensiune);

unde dimensiune este numarul maxim de caractere care va fi citit.

La citire, adaugarea terminatorului NULL este realizata in mod automat. Daca dorim citirea caracter cu caracter, acest lucru este posibil (in stilul citirii vectorilor), dar trebuie sa avem grija de terminatorul NULL, functiile pe siruri de caractere tinand cont de acesta.

7.3 Accesul la caracterele unui sir

Daca dorim parcurgerea unui sir de caractere, flosim urmatoare schema generala:

Observam conditia de oprire S[i], echivalenta cu S[i]!=NULL. In cuvinte, schema de mai sus ar fi: „de la primul caracter si pana la caracterul NULL, foloseste caracterul curent”. Alte scheme utile:

Verificarea daca un caracter este sau nu cifra:

Verificarea daca un caracter este sau nu litera mare:

- 95 -

for(i=0;S[i];i++)foloseste(S[i]);

If ((c>=’0’)&&(c<=’9’))foloseste(c);

If ((c>=’A’)&&(c<=’Z’))foloseste(c);

Introducere in ANSI C++

P4. Se citeste un sir de caractere fara spatii. Inlocuiti literele mici cu litere mari

si invers.

Introduceti un sir: abcDEF123

ABCdef123

P5. Se citeste un sir de caractere cantinand cuvinte separate de unul sau mai

multe spatii. Afisati numarul de cuvinte.

Introduceti un sir: Ana are laptop

Numarul de cuvinte:3

- 96 -

#include <iostream>using namespace std;

int main(){ char S[100]; int i; cout << "Introduceti un sir: "; cin >> S; for(i=0;S[i];i++){ if ( (S[i]>='a')&&(S[i]<='z') ) S[i] = S[i] - 'a' + 'A'; else if ( (S[i]>='A')&&(S[i]<='Z') ) S[i] = S[i] - 'A' + 'a'; } cout << S;}

#include <iostream>using namespace std;

int main(){ char S[100]; int i,ct; cout << "Introduceti un sir: "; cin.getline(S,100); if (S[0]!=' ') ct=1; else ct=0; for(i=1;S[i];i++) if ( (S[i-1]==' ') && (S[i]!=' ') ) ct++; cout << "Numarul de cuvinte:"<<ct;}

Introducere in ANSI C++

In problema P5, numarul de cuvinte este determinat numarand cate succesiuni de forma spatiu-caracter avem in sir, primul cuvant fiind numarat separat daca sirul nu incepe cu spatiu.

7.4 Functii specifice sirurilor de caractere

In biblioteca standard string.h sunt definite o serie de functii utile pe siruri de caractere . Cele mai utilizate sunt date in Tabelul 7.1:

Functie Efect Exemple

strlen(S)Returneaza lungimea sirului dat ca paramentru

strlen("trei") ­> 4

strcpy(S1,S2) Copiaza S2 peste S1 strcpy(S,"abcd")

strcat(S1,S2) Concaterneaza S2 la S2 strcpy(S,"Imi place ");strcat(S,"C++");

strcmp(S1,S2)

Compara lexicografic sirurile date ca parametri. Returneaza0 daca S1==S2< 0 daca S1<S2> 0 daca S1>S2

if (strcmp(S1,S2)==0)    cout << "Egale";else    cout << "Diferite";

atoi(S)Returneaza numarul intreg corespunzator sirului dat

i=atoi("123")

strchr(S,c)strstr(S1,S2)

Returneaza o valoare pozitiva daca un caracter (respectiv sir) apare intr-un sir, 0 in caz contrar

if (strchr(S,c))   cout << "L­am gasit!"

itoa(S,nr,baza)

Depoziteaza in S sirul corespunzator numarului nr, in baza baza

itoa(S,"123",10)

Tabelul 7.1

- 97 -

Introducere in ANSI C++

Observatii:

– itoa nu este implementata in standardul ANSI C++, dar este suportata de

multe compilatoare.

Mai departe dam implementari posibile ale acestor functii, rezolvand problemele date atat folosind functiile de bilbioteca cat si implementand din cod.

P6. Se citeste un sir de caractere. Afisati lungimea acestuia.

Introduceti un sir: Ana are laptop

Lungimea:14

Lungimea (strlen):14

Observam ca functia strlen nu contorizeaza si terminatorul NULL. Nu este recomandata parcurgerea unui sir de caractere sub forma

for(i=0;i<strlen(S);i++) ...

apelul functie strlen parcurgand de fiecare data tot sirul, astfel scazand eficienta algoritmului.

- 98 -

#include <iostream>#include <string.h>using namespace std;

int main(){ char S[100]; int i; cout << "Introduceti un sir: "; cin.getline(S,100); for(i=0;S[i];i++);// i va contine la sfarsit lungimea cout << "Lungimea:"<< i << "\n"; cout << "Lungimea (strlen):"<< strlen(S); }

Introducere in ANSI C++

P7. Se citeste un sir de caractere. Copiati-l in alt sir.

Introduceti un sir: Ana are laptop

X:        :Ana are laptop

Y (strcpy):Ana are laptop

In cazul in care facem copierea din cod, trebuie sa avem grija de terminator, in caz contrar alte operatii pe sir, cum ar fi afisarea, nu vor actiona dupa asteptari.

Atribuire directa intre doua siruri de caractere (posibila in alte limbaje), de forma: S1=S2;

va da un mesajul de eroaren1=atoi(S1);

p07.cpp:21: error: ISO C++ forbids assignment of arrays

P8. Concaternati doua siruri de caractere.

In implementarea urmatoare am comentat apelul functiei strcat. Analog exemplelor anterioare, observam ca folosirea functiei de biblioteca simplifica mult programul. O atribuire de forma: X=S1+S2; va da mesajul de eroare:

p09.cpp:23: error: invalid operands of types ‘char [100]’ and ‘char [100]’ to binary ‘operator+

- 99 -

#include <iostream>#include <string.h>using namespace std;

int main(){ char S[100],X[100],Y[100]; int i; cout << "Introduceti un sir: "; cin.getline(S,100); for(i=0;S[i];i++) X[i]=S[i]; // copiere din cod X[i]='\0'; strcpy(Y,S); cout << "X: :"<< X << "\n"; cout << "Y (strcpy):"<< Y; }

Introducere in ANSI C++

Introduceti numele: Ana

Salut Ana

Exista posibilitatea de a compara doua siruri de caractere. In acest caz se va tine cont de ordinea lor lexicografica (cea din dictionar), iar litera mare va fi considerata mai mica fata de litera mica corespunzatoare (conform codurilor ASCII). Urmatoare exemple sunt sugestive:

"A" < "a"

"unu" == "unu"

"pom" < "pomisor"

"trei" > "patru"  !!!!

P9. Se citesc doua siruri de caractere.Verificati ordinea lor lexicografica.

Am comentat in acest caz varianta in care am rezolvat fara apelul functiei strcmp.

- 100 -

#include <iostream>#include <string.h>using namespace std;

int main(){ char nume[100],S[100]; int i,j; cout << "Introduceti numele: "; cin.getline(nume,100); strcpy(S,"Salut "); j=strlen(S); for(i=0;nume[i];i++) S[j++]=nume[i]; S[j]='\0'; // strcat(S,nume); cout << S;}

Introducere in ANSI C++

Introduceti sirul 1: trei

Introduceti sirul 2: patru

"trei" este mai mare ca "patru"

P10. Se citesc doua siruri de caractere reprezentand numere intregi. Faceti

suma lor, apoi afisati pe ecran rezultatul.

Introduceti numarul 1: 123

Introduceti numarul 2: 234

123 + 234 = 357

- 101 -

#include <iostream>#include <string.h>using namespace std;

int main(){ char S1[100],S2[100]; int i,j; cout << "Introduceti sirul 1: "; cin.getline(S1,100); cout << "Introduceti sirul 2: "; cin.getline(S2,100); // for(i=0;S1[i] && S2[i] && S1[i]==S2[i];i++);// if (S1[i]==S1[i]) cout << "EGALE!"// else if (S1[i]<S2[i]) cout << "S1<S2"// else cout << "S1>S2"; cout << "\""<<S1<<"\"";; if (strcmp(S1,S2)==0) cout << " este egal cu "; else if (strcmp(S1,S2)<0) cout << " este mai mic ca "; else cout << " este mai mare ca "; cout << "\""<<S2<<"\"";; }

#include <iostream>#include <string.h>using namespace std;

int main(){ char S1[10],S2[10]; int rez,n1,n2; cout << "Introduceti numarul 1: "; cin.getline(S1,10); cout << "Introduceti numarul 2: "; cin.getline(S2,10); n1=atoi(S1); n2=atoi(S2); rez=n1+n2; cout << S1 << " + " << S2 << " = " << rez; }

Introducere in ANSI C++

P11. Verificati daca un nume apare intr-un sir.

Introduceti numele: Radu

Introduceti sirul: Toni Alina Radu Cristi

"Radu" apare in sirul "Toni Alina Radu Cristi"

7.5 Matrici de caractere

Utilizarea unui numar mare de siruri de caractere intr-o aplicatie implica folosirea unui sir de siruri de caractere. Urmatoarele exemple arata definirea si initalizarea unei matrici de caractere:

char S[10][10]={"Ana", "are","laptop"};

Pentru exemplul de mai sus, S[0] este "Ana", S[1] este "are", S[2] este "laptop", iar S[3],...,S[9] sunt siruri vide.

In general, fiecare rand al unei matrici de caractere reprezinta un sir de caractere.

- 102 -

#include <iostream>#include <string.h>using namespace std;

int main(){ char nume[10],S[100]; int rez,n1,n2; cout << "Introduceti numele: "; cin.getline(nume,10); cout << "Introduceti sirul: "; cin.getline(S,100); if (strstr(S,nume)) cout << "\"" << nume << "\" apare in sirul \"" << S << "\""; else cout << "\"" <<nume<< "\" nu apare in sirul \"" << S << "\""; }

Introducere in ANSI C++

P12. Se citesc doua siruri reprezentand zile ale saptamanii. Folosind o matrice

de caractere initializata cu zilele saptamanii, afisati zilele curinse intre cele citite

de la tastatura.

Introduceti ziua 1: marti

Introduceti ziua 2: joi

­­­­­­­­­­­­­­­­­­­­­­­

marti

miercuri

joi

- 103 -

#include <iostream>#include <string.h>using namespace std;

int main(){ char zile[7][10]={ "luni", "marti", "miercuri", "joi", "vineri", "sambata", "duminica" }; char z1[10],z2[10]; int poz1,poz2,i; cout << "Introduceti ziua 1: "; cin.getline(z1,10); cout << "Introduceti ziua 2: "; cin.getline(z2,10); poz1=poz2=-1; for(i=0;i<7;i++){ if (strcmp(z1,zile[i])==0) poz1=i; if (strcmp(z2,zile[i])==0) poz2=i; } cout << "-----------------------\n"; if ((poz1==-1)||(poz2==-1)) cout << "Zile incorecte!"; else for(i=poz1;i<=poz2;i++) cout << zile[i] << "\n";}

Introducere in ANSI C++

7.6 Alte aplicatii

P13. Se citeste un sir de caractere de la tastatura. Afisati toate prefixele si

sufixele acestuia.

abcdef

Prefixe:

abcdef abcde abcd abc ab a

Sufixe:

abcdef bcdef cdef def ef f 

- 104 -

#include <iostream>#include <string.h>using namespace std;

int main(){ char cuvant[100]; char copie[100]; int i,j; cin >> cuvant; cout << "Prefixe:\n"; strcpy(copie,cuvant); for(i=strlen(cuvant);i>0;i--){ copie[i]='\0'; // "mutam" sfarsitul cuvantului la stanga cout << copie<< " "; } cout << "\nSufixe:\n"; for(i=0;cuvant[i];i++){ for(j=i;cuvant[j];j++) cout << cuvant[j]; cout << " "; }}

Introducere in ANSI C++

P14. Se citesc de la tastatura siruri de caractere pana la intalnirea sirului

"stop". Afisati cuvantul de lungime maxima.

mic mare scurt lung stop

Cuvantul cel lung: scurt

- 105 -

#include <iostream>#include <string.h>using namespace std;

int main(){ char S[100],Smax[100]; cin >> S; strcpy(Smax,S); while (strcmp("stop",S)){ if (strlen(S)>strlen(Smax)) strcpy(Smax,S); cin >>S; } cout << "Cuvantul cel lung: " << Smax;}

Introducere in ANSI C++

7.7 Probleme propuse

Pp1. Se citesc de la tastatura doua siruri de caractere continand doar cifre. Considerand sirurile ca fiind numere in baza 10, calculati suma lor in alt sir.

Pp2. Se citeste de la tastatura un sir de caractere continand litere mici si spatii. Afisati cuvintele conponente, cate unul pe un rand.

Pp3. Un sir de caractere contine doar litere mici si litere mari. Prin operatie elementara intelegem transformarea din litera mica in litera mare sau invers. Care este numarul minim de operatii elementare astfel incat sa obtinem din sirul initial unul avand toate literele mici sau toate literele mari.

Pp4. Se citesc de la tastatura cuvinte (fara spatii), pana la citirea cuvantului "gata". Afisati cel mai lung cuvant care contine doar vocale.

Pp5. Se citeste un sir S, continand cuvinte separate de exact un spatiu si doua cuvinte, C1 si C2. In locuiti toate aparitiile cuvantului C1 din testul S cu cuvantul C2.

Pp6. O matrice de caractere contine cate un cuvant pe un rand. Sortati cuvintele lexicografic, apoi afisati-le pe ecran.

- 106 -

Introducere in ANSI C++

Capitolul 8 – Structuri si alte tipuri utilizator

Dupa cum am vazut, un vector grupeaza sub un nume mai multe valori de acelasi tip. In acest capitol vom urmarii gruparea informatiilor corelate de tipuri diferite.

8.1 Structuri

Gruparea informatiilor corelate de tipuri diferite se face prin utilizarea structurilor. Avem:

struct nume_structura{

declaratii_de_variabile;

} lista_variabile;

Atat nume_structura cat si lista_variabile sunt optionale, dar pot lipsi ambele in acelasi timp.

In C++, prin aceasta, nume_structura devine un nou tip de data, tip care poate fi folosit la declararea unor variabile.

nume_structura lista_variabile;

Sintaxa permite declararea directa, in momentul definirii unei structuri a unor variabile de acel tip.

Daca lipseste nume_structura, avem o structura omonima:

struct {

declaratii_de_variabile;

} lista_variabile;

In acest caz vom folosi in program doar variabilele definite, neexistand posibilitatea definirii altor variabile de forma data.

Variabilele definite in bloc repezinta datele ce dorim sa le grupam.

Accesul la elementele unei structuri se face folosind operatorul ‘.’:

variabila.nume_data

- 107 -

Introducere in ANSI C++

Exista posibilitatea de a face atribuiri intre doua variabile de tip structura, daca cele doua variabile sunt de acelasi tip. Deci daca avem:

nume_structura V1,V2;

atunci atribuirea V1=V2; este valida.

Initializarea unei structuri se face intr-o maniera analoaga initializarii vectorilor.

P1. Definiti structura persoana, continand (nume, prenume, varsta). Definiti o

variabila initializata de tipul persoana, mariti varsta cu 1, apoi afisati pe ecran.

Gates Gheorghe 31

In memorie o variabila de tip structura ocupa o zona continua de memorie. Figura 8.1 ilustreaza reprezentarea in memorie a unei structuri.

Figura 8.1

Tipurile de date definite prin cuvantul cheie struct pot fi transmise ca parametri sau pot fi returnate din functii.

- 108 -

#include <iostream>using namespace std;

// definirea structurii cerutestruct persoana{ char nume[20]; char prenume[20]; int varsta; };

int main(){ persoana P={"Gates","Gheorghe",30}; P.varsta++; cout << P.nume << " " << P.prenume << " " << P.varsta;}

struct persoana{nume char[20];int varsta;float inaltime;

};

nume

varstainaltime

20 bytes

sizeof(int) bytes

sizeof(int) bytes

Introducere in ANSI C++

P2. Definiti structura complex (re,im). Implementati functiile suma, produs si

modul.

S=6+i*1

P=7+i*9

m=2.23607

- 109 -

#include <iostream>#include <math.h>using namespace std;

struct complex{ float re; float im;};

complex suma(complex,complex);complex produs(complex,complex);float modul(complex);

int main(){ complex z1={1,2},z2={5,-1},S,P; // z1= 1+2i si z2=5-i float m; S=suma(z1,z2); P=produs(z1,z2); m=modul(z1); cout << "S=" << S.re << "+i*" << S.im << endl; cout << "P=" << P.re << "+i*" << P.im << endl; cout << "m=" << m;}

complex suma(complex x, complex y){ complex T; T.re = x.re + y.re; T.im = x.im + y.im; return T;}

complex produs(complex x, complex y){ complex T; T.re = x.re*y.re - x.im*y.im; T.im = x.re*y.im + x.im*y.re; return T;}

float modul(complex x){ return sqrt(x.re*x.re + x.im*x.im);}

Introducere in ANSI C++

Putem defini vectori de structuri sau structuri care contin alte structuri.

P3. Definiti structura persoana (nume, data_nasterii), unde data nasterii este o

alta structura (zi,luna,an). Cititi de la tastatura 3 persoane, apoi afisati-le in

ordine crescatoare dupa anul nasterii.

Ana  12 8 2003

Luci 21 5 1998

Tom  5 12 2000

Luci  21/5/1998

Tom  5/12/2000

Ana  12/8/2003

- 110 -

#include <iostream>using namespace std;

struct data{ int zi,luna,an;};

struct persoana{ char nume[20]; data datan;};

int main(){ persoana P[10],t; int j,i,nr=3; for(i=0;i<nr;i++){ cin >> P[i].nume; cin >> P[i].datan.zi >> P[i].datan.luna >> P[i].datan.an; } for(i=0;i<nr-1;i++) for(j=i+1;j<nr;j++) if (P[i].datan.an > P[j].datan.an){ t=P[i]; P[i]=P[j]; P[j]=t; }

for(i=0;i<nr;i++){ cout << P[i].nume << " " << P[i].datan.zi << "/"; cout << P[i].datan.luna << "/" << P[i].datan.an << endl; } }

Introducere in ANSI C++

8.2 Uniuni

Dupa cum am vazut, in cazul structurilor se aloca memorie pentru fiecare variabila declarata in structura, cantitatea totala de memorie obtinandu-se adunand cantitatea de memorie necesara fiecarei variabile componente in parte.

In cazul care grupam date folosind uniuni, datele interne vor ocupa aceeasi zona de memorie, in total uniunea ocupand cat ocupa cea mai mare (dinpunct de vedere al necesarului de memorie) variabila componenta.

Sintaxa:

union nume_structura{

declaratii_de_variabile;

} lista_variabile;

P4. Definiti o uniune constinand un intreg si un sir de 4 caractere. Folositi

uniunea pentru a modifica octetii din componenta intregului.

- 111 -

Introducere in ANSI C++

0000h = 0

F000h = 4278190080

0F00h = 16711680

00F0h = 65280

000Fh = 255

- 112 -

#include <iostream>using namespace std;

union intreg{ unsigned int i; char c[4];};

int main(){ intreg I; I.i=0; cout << "0000h = " << I.i << endl; I.c[3]=255; cout << "F000h = " << I.i << endl; I.c[3]=0; I.c[2]=255; cout << "0F00h = " << I.i << endl; I.c[2]=0; I.c[1]=255; cout << "00F0h = " << I.i << endl; I.c[1]=0; I.c[0]=255; cout << "000Fh = " << I.i; }

Introducere in ANSI C++

P5. Definiti o uniune prin care putem memora oricare dintre figurile geometrice

urmatoare: cerc, triunghi, dreptunghi.

Cerc:10,10,5

Dreptunghi: (1,2)  (15,20) 

- 113 -

#include <iostream>using namespace std;

struct punct{ int x,y;};

struct cerc{ punct P; int r;};

struct triunghi{ punct A,B,C;};

struct dreptunghi{ punct A,B;};

union figura{ cerc C; triunghi T; dreptunghi D;};

int main(){ figura F; F.C.P.x=10; F.C.P.y=10; F.C.r=5; // cerc cout << "Cerc:" << F.C.P.x <<"," << F.C.P.y <<"," << F.C.r << endl; F.D.A.x=1; F.D.A.y=2; // dreptunghi F.D.B.x=15; F.D.B.y=20; cout << "Dreptunghi: (" << F.D.A.x <<"," << F.D.A.y <<") "; cout << " (" << F.D.B.x <<"," << F.D.B.y <<") ";

}

Introducere in ANSI C++

8.3 Campuri de biti

C++ permite alocarea de variabile la nivel de bit.

struct nume_structura{

unsigned var1:nr_biti;

unsigned var2:nr_biti;

...

}lista_variabile;

Tipul variabilelor din campul de biti trebuie sa fie unsigned int.

P6. Definiti si utilizati un camp de biti pentru stocarea unei date calendaristice.

29 4 73

Pentru acest exemplu avem urmatoare situatie in memorie:

Figura 8.2

- 114 -

#include <iostream>using namespace std;

struct data{ unsigned zi:5; unsigned luna:4; unsigned an:7; };

int main(){ data D={29,4,73}; cout << D.zi << " " << D.luna << " " << D.an;}

struct data{unsigned zi:5;unsigned luna:4;unsigned an:7;

};

zi luna0 15

an4 5 8 9

Introducere in ANSI C++

8.4 Tipul enumerat

Pentru o mai mare claritate, putem defini tipuri enumerate:

enum nume_enumerare {lista_nume};

Analog structurilor, nume_enumerare devine un tip de data care poate fi folosit ulterior. Implicit, numele dintre acolade vor fi asociate cu valori de la 0 din 1 in 1. Datele de tip enumerare sunt considerate de tip int.

Putem forta valorile dorite prin atribuiri.

P7. Definiti si utilizati o enumerare pentru lucrul cu zilele saptamanii.

treaba treaba treaba treaba treaba liber liber 

- 115 -

#include <iostream>using namespace std;

enum saptamana {luni, marti, miercuri, joi, vineri, sambata, duminica};

void Mesaj(int); int main(){ saptamana S; int i,ct; for(i=luni; i<=duminica; i++) Mesaj(i); }

void Mesaj(int S){ if ((S>=luni)&&(S<=vineri)) cout << "treaba "; else cout << "liber ";}

Introducere in ANSI C++

P8. Definiti si utilizati o enumerare care asociaza notele si calificativele.

Dati o nota:8

e ok!

- 116 -

#include <iostream>using namespace std;

enum calificative { insuficient=4, suficient=5, bine=7, excelent=10 };

int main(){ int nota; cout << "Dati o nota:"; cin >> nota; if (nota<=insuficient) cout << "mai incearca!"; else if (nota<=suficient) cout << "la limita!"; else if (nota==excelent) cout << "perfect!"; else cout << "e ok!"; }

Introducere in ANSI C++

8.5 Probleme propuse

Pp1. Definiti o structura de lucru cu numare rational si implementati operatiile de adunare, scadere si inmultire.

Pp2. O structura contine numele si numarul de selectii ale unui jucator de fotbal. Definiti un vector continand elemente de acest tip, cititi de la tastatura 5 valori, apoi afisati in ordine crescatoare dupa numarul de selectii.

Pp3. O structura contine coordonatele (intregi) stanga sus si dreapta jos ale unui dreptunghi. Se citeste o matrice de dreptunghiuri astfel definite. Afisati dreptunghiul cu aria maxima.

Pp4. O structura contine numele si data nasterii unei persoane, sub forma an, luna,zi. Se citeste de la tastatura un vector de elemente de tipul definit anterior. Sortati crescator dupa varsta, apoi afisat pe ecran.

- 117 -

Introducere in ANSI C++

Capitolul 9 – Functii

Daca pana acum am folosit la scrierea programelor noastre doar functia main, functie in care am scris tot codul dorit, este evident ca in cazul problemelor complexe este necesare o impartire in module a programului. Modularizarea poate fi realizata in C/C++ folosind functii. O functie reprezinta o colectie de declaratii si instructiuni de sine statatoare care rezolva o anumita subproblema. Vom prezenta mai departe modul de utilizare a functiilor (prototip, definire, parametri de intrare, parametri de iesire, valoare returnata, apel) si vom clasifica functiile in functie de utilizarea lor.

9.1 Generalitati

Forma generala a unui algoritm este urmatoarea:

Figura 9.1

Cum o functie modeleaza un algoritm (subalgoritm), forma generala a unei functii este data de:

Figura 9.2

Inainte de utilizarea unei functii, aceasta trebuie sa fie cunoscuta de catre compilator. Acest lucru se poate realiza definind functia inainte de locul apelului. A doua modalitate, folosita in acest material, este folosirea prototipurilor. Un prototip reprezinta pentru o functie ceva similar declaratiei de variabila pentru o variabila. Folosind un prototip vom descrie comportamentul functiei, fara a scrie rezolvarea, altfel spus, vom descrie datele de intrare si cele de iesire din functie.

Forma generala a unui prototip al unei functii asociata algoritmului general din figura 9.2 este:

- 118 -

Algoritmdate de intrare date de iesire

Functiedate de intrare date de iesire

Introducere in ANSI C++

tip_returnat nume_functie(lista_parametri);

Numele functie poate fi orice identificator valid. Tipul returnat si lista parametrilor vor da catalogarea din subcapitolele urmatoare.

Definirea functiei, poate avea loc in orice zona a codului sursa, mai putin in ainteriorul altei functii. Definirea unei functii este de forma

functie = antet + bloc

sau, pe larg:

tip_returnat nume_functie(lista_parametri){

declaratii si instructiuni

}

Apelul sau utilizarea efectiva a functie are loc sub forma:

nume_functie(lista_parametri);

Pe locul apelului va aparea o valoare de tip tip_returnat;

9.2 Functii care nu primesc si nu returneaza nimic

Forma generala a unei astfel de functii este:

Figura 9.3

Specificam lipsa valorii returnate folosind tipul de date void.

Prototipurile aasociate acestui caz sunt de forma:

void nume_functie(); sau

void nume_functie(void);

Observam ca, in cazul parametrilor, tipul void poate sa lipseasca. Daca in loc de tip returnat nu scriem nimic, tipul returnat implicit este int.

Apelul functiei, pentru acest caz, va fi de forma:

nume_functie();

- 119 -

Functie

Introducere in ANSI C++

Parantezele de dupa numele functiei nu sunt optionale.

In general acest tip de functii vor afisa ceva pe ecran.

Faptul ca functia nu primeste date de intrare nu ne impiedica sa folosim o serie de variabile de lucru pentru rezolvarea problemei, variabile declarate in cadrul blocului functiei.

P1. Realizati o functie care afiseaza un mesaj de intampinare pe ecran.

*******************

* Bine ati venit! *

*******************

P2. Realizati o functie care sterge ecranul prin trecerea la rand nou de 30 de

ori, apoi afisati, folosind o alta functie, un bradulet pe ecran.

- 120 -

#include <iostream>using namespace std;

void Mesaj(void);

int main(){ Mesaj();}

void Mesaj(){ cout << "*******************" << endl; cout << "* Bine ati venit! *" << endl; cout << "*******************" << endl;}

Introducere in ANSI C++

    *

   ***

  *****

 *******

    |

9.3 Functii care nu returneaza nimic, dar primesc date de

intrare

Analog cazului anterior acest ip de functii vor afisa de obicei rezultatele pe ecran. Forma generala este:

Figura 9.4

Prototipul:

void nume_functie(lista_parametri);

iar apelul:

- 121 -

#include <iostream>using namespace std;

void Sterge();void Bradulet();

int main(){ Sterge(); Bradulet();}

void Sterge(){ int i; for(i=1;i<=30;i++) cout << endl;}

void Bradulet(){ cout << " * " << endl; cout << " *** " << endl; cout << " ***** " << endl; cout << " ******* " << endl; cout << " | " << endl;}

Functiedate de intrare

Introducere in ANSI C++

nume_functie(lista_parametri);

Vom descrie pentru acest caz, pas cu pas, ce se intampla in cazul apelului unei functii. Fie urmatoare problema:

P3. Realizati o functie care, primind ca parametri doua numere intregi a si b,

afiseaza pe ecran valorile pare din intervalul [a,b].

246

Se observa ca atunci cand scriem prototul functiei se specifica doar tipul parametrilor de intrare. La scrierea antetului vom da si numele parametrilor de intrare, nume prin care vor fi accesati acestia in blocul functiei.

Stiva, o zona de memorie speciala in care sunt salvate, in momentul apelului o serie de informatii despre functia apelata, cum ar fi adresa de revenire, parametri si variabilele declarate in functie (variabile locale). Avem urmatorii pasi:

Pas 1. Inainte de apel:

Figura 9.5

- 122 -

#include <iostream>using namespace std;

void afisare(int,int); // prototip

int main(){ afisare(2,7); // apel}

void afisare(int a, int b){ // antet int i; for(i=a;i<=b;i+=2) cout << i; }

int main(){

afisare(2,7);

}

vf

StivaPozitia in program

Introducere in ANSI C++

Pas 2. Apelul (intrarea in functie)

Figura 9.5

In momentul apelului, se aloca pe stiva variabilele ce reprezinta parametri de intrare si variabilele definite in functie. Parametri a si b ai functiei (numiti parametri formali) vor prelua valorile date la apel (parametri actuali). In cazul in care apelul are ca parametri expresii, acestea trebuie sa aiba un tip compatibil cu al parametrilor formali. In acest caz, are loc evaluarea expresiei, rezultatul fiind atribuit parametrului corespunzator. Acest tip de apel poarta numele de apel prin valoare.

Pas 3. Inainte de revenire

Figura 9.6

- 123 -

int main(){

afisare(2,7);

}

void afisare(int a, int b){

int i;

...

}

vf

StivaPozitia in program

27? i

ba

int main(){

afisare(2,7);

}

void afisare(int a, int b){

int i;

...

}

vf

StivaPozitia in program

278 i

ba

Introducere in ANSI C++

Observam modificarea valorii variabilei locale i, conform instructiunilor executate.

Pas 4. Revenirea din functie

Figura 9.7

La iesire din functie, stiva revine la nivelul anterior apelului, iar variabilele locale si parametri formali dispar.

Programul continua cu prima instructiune de dupa apel.

P4. Realizati o functie care afiseaza un bradulet pe ecran, cu dimensiunea data

de un parametru.

- 124 -

int main(){

afisare(2,7);

}

vf

StivaPozitia in program

Introducere in ANSI C++

  *

 ***

*****

  |

    *

   ***

  *****

 *******

*********

    |

- 125 -

#include <iostream>using namespace std;

void Bradulet(int); void Spatii(int);void Stelute(int);

int main(){ Bradulet(3); Bradulet(5);}

void Spatii(int n){ int i; for(i=1;i<=n;i++) cout << " ";}

void Stelute(int n){ int i; for(i=1;i<=n;i++) cout << "*";}

void Bradulet(int n){ int i; cout << endl; for(i=1;i<=n;i++){ Spatii(n-i); Stelute(2*i-1); cout << endl; } Spatii(n-1); cout << "|" ;}

Introducere in ANSI C++

P5. Folosind functii, afisati pe ecran urmatoare piramida de numere:

11 2 1

1 2 3 2 1....................

    1

   121

  12321

 1234321

123454321

- 126 -

#include <iostream>using namespace std;

void Spatii(int);void Crescator(int); void Descrescator(int);void Piramida(int);

int main(){ Piramida(5);}

void Piramida(int n){ int i; for(i=1;i<=n;i++){ Spatii(n-i); Crescator(i); Descrescator(i-1); cout << endl; }}void Crescator(int n){ int i; for(i=1;i<=n;i++) cout << i;}void Spatii(int n){ int i; for(i=1;i<=n;i++) cout << " ";}void Descrescator(int n){ int i; for(i=n;i>=1;i--) cout << i;}

Introducere in ANSI C++

9.4 Functii care nu primesc date de intrare dar returneaza

o valoare

Forma generala este:

Figura 9.8

Prototipul:

tip_returnat nume_functie(); sau:

tip_returnat nume_functie(void); , 

Valoarea va fi returnata din functie folosind instructiunea return, moment in care se iese din functie. La revenirea din apel, pe locul apelului "apare" valoarea returnata. Forma generala a definirii functiei:

tip_returnat nume_functie(){

....

return expresie;

}

unde expresie este convertibil la tip_returnat.

Exemple de apeluri:

var=nume_functie(); // var compatibil cu tip_returnat

if (expresie == nume_functie()) ...

cout << nume_functie();

P6. Realizati o functie care returneaza valoarea lui Pi cu doua zecimale exacte.

- 127 -

Functiedate de iesire

Introducere in ANSI C++

3.14

P7. Realizati o functie care citeste de la tastatura valori intregi (<1000) pana la

intalnirea valorii 1000 si returneza minimul acestor valori.

12 45 29 1000

45

- 128 -

#include <iostream>using namespace std;

float Pi();

int main(){ cout << Pi();}

float Pi(){ return 3.14;}

#include <iostream>using namespace std;

int max();

int main(){ cout << max();}

int max(){ int X,mx; cin >> X; mx=X; while(X<1000){ if (X>mx) mx=X; cin >> X; } return mx;}

Introducere in ANSI C++

9.5 Functii care primesc un numar oarecare de parametri

de intrare si returneaza o singura valoare

Este probabil cea mai utilizata varianta de functie.

Forma generala:

Figura 9.9

Prototipul:

tip_returnat nume_functie(lista_parametri);

Exemple de apeluri:

var = nume_functie(lista_parametri); sau:

if (nume_functie(lista_parametri)==expresie) sau:

cout << nume_functie(lista_parametri)

Valoarea este returnata din functie prin folosirea instructiunii return. La intalnirea acestei instructiuni, se revine automat din functie, iar pe locul apelului va aparea valoarea returnata.

P8. Realizati urmatoarele functii:

• max(a,b)

• modul(a)

• min(a,b,c)

- 129 -

Functiedate de intrare data de iesire

Introducere in ANSI C++

a=­6

b=3

c=1

max(­6,3)=3

|­2|=2

min=­2

Observam diferite modalitati de a apela functiile cerute in enunt.

P9. Realizati functiile cu urmatoarele prototipuri:

• int sumac(int); // suma cifrelor unui numar

- 130 -

#include <iostream>using namespace std;

int max(int, int);int modul(int);float min(float, float, float);

int main(){ int a,b,c; float x; cout << "a="; cin >>a; cout << "b="; cin >>b; cout << "c="; cin >>c; cout << "max(" << a << "," << b << ")=" << max(a,b) << endl; cout << "|"<< a+b+c << "|=" << modul(a+b+c)<< endl; x=min(float(a)/b , float(b)/c, float(c)/a); cout << "min=" << x<< endl;}

int max(int a, int b){ if (a>b) return a; else return b;}

int modul(int x){ return (x>0)?x:-x;}

float min(float x, float y, float z){ if (x<y) return (x<z)?x:z; else return (y<z)?y:z;}

Introducere in ANSI C++

• int sumadiv(int); // suma divizorilor unui numar

• int primac(int); // prima cifra a unui numar

• int fact(int); // factorialul unui numar

a=5123

- 131 -

#include <iostream>using namespace std;

int sumac(int); // suma cifrelor unui numarint sumadiv(int); // suma divizorilor unui numarint primac(int); // prima cifra a unui numarint fact(int); // factorialul unui numar

int main(){ int a,b; cout << "a="; cin >> a; cout << "b="; cin >> b; cout << "sumac(" << a << ")=" << sumac(a) << endl; cout << "sumadiv(" << a << ")=" << sumadiv(a) << endl; cout << "primac(" << a << ")=" << primac(a) << endl; cout << "fact(" << b << ")=" << fact(b) << endl;}

int sumac(int n){ int S; S=0; for(;n>0;n/=10) S+=n%10; return S;}int sumadiv(int n){ int d,S; S=0; for(d=1;d<=n;d++) if (n%d==0) S=S+d; return S;}int primac(int n){ while (n>=10) n=n/10; return n;}int fact(int n){ int i,P; P=1; for(i=1;i<=n;i++) P*=i; return P;}

Introducere in ANSI C++

b=4

sumac(5123)=11

sumadiv(5123)=5280

primac(5123)=5

fact(4)=24

P10. Realizati urmatoarele functii ce returneaza valori logice:

bool prim(int); // verifica daca un numar este prim sau nu

bool perfect(int); // verifica daca un numar este egal cu suma divizorilor sai

x=28

- 132 -

#include <iostream>using namespace std;

bool prim(int);bool perfect(int);int sumad(int);

int main(){ int x; cout << "x="; cin>>x; if (prim(x)==true) cout << x << " este prim \n"; else cout << x << " nu este prim \n"; if (perfect(x)) cout << x << " este perfect \n"; else cout << x << " nu este perfect \n"; }

bool prim(int n){ int d; for(d=2;d<=n/2;d++) if (n%d==0) return false; return true;}bool perfect(int n){ return sumad(n)==n;}int sumad(int n){ int S,d; for(S=0,d=1;d<=n/2;d++) if (n%d==0) S+=d; return S;}

Introducere in ANSI C++

28 nu este prim

28 este perfect

P11. Folosind functii, afisati cel mai mare numar prim mai mic ca n (numar

intreg citit de la tastatura).

x=100

Cel mai mare numar prim mai mic ca 100 este 97

9.6 Functii ce returneaza mai multe valori

Forma generala:

Figura 9.10

Vom prezenta in continuare doua variante de realizare.

- 133 -

#include <iostream>using namespace std;

bool prim(int);int prim_mic(int);

int main(){ int x; cout << "x="; cin>>x; cout << "Cel mai mare numar prim mai mic ca " << x; cout << " este " << prim_mic(x); }

bool prim(int n){ int d; for(d=2;d<=n/2;d++) if (n%d==0) return false; return true;}

int prim_mic(int n){ while(!prim(n)) n--; return n;}

Functiedate de intrare date de iesire

Introducere in ANSI C++

O prima varianta este folosirea structurilor. Vom grupa sub numele unei structuri datele care vor fi returnate de catre functie, iar prototipul va fi de forma:

nume_structura  nume_functie(lista_parametri);

Apelul va fi de forma:

nume_structura var;....var = nume_functie(lista_parametri);

Definirea functiei va respecta urmatoarea structura:

nume_structura  nume_functie(lista_parametri){nume_structura  T;

...return T;}

Structura T din cadrul functiei va contine datele de returnat.

P12. Realizati o functie care calculeaza suma si produsul a doua numere reale.

- 134 -

Introducere in ANSI C++

8 15

A doua varianta este folosirea referintelor (2.5). In acest caz, prototipul ca fi de forma:

void nume_functie(lista_parametri_intrare, lista_parametri_iesire);

sau:

void nume_functie(lista_parametri_intrare_si_iesire);

Apelul:

nume_functie(lista_parametri);

Dupa cum am vazut, transferul parametrilor prin valoare nu duce la modificarea parametrilor actuali, chiar daca acestia sunt variabile (se lucreaza cu copii pe stiva). In cazul in care dorim modifiarea lor, vom folosi parametri referinte. In acest caz, parametrul formal devine un alias al celui actual, medificarea acestuia modificand de fapt paramatrul actual.

- 135 -

#include <iostream>using namespace std;

struct doua_numere{ float suma; float produs; };

doua_numere sp(float,float);

int main(){ doua_numere a; a=sp(3,5); cout << a.suma << " " <<a.produs;}

doua_numere sp(float a, float b){ doua_numere T; T.suma=a+b; T.produs=a*b; return T;}

Introducere in ANSI C++

Evident, in acest caz paramatri actuali trebuie sa fie variabile de un tip compatibil parametrul formal corespunzator. Valorile vor fi deci "returnate" prin modificarea variabilelor din apel.

P13. Realizati o functie care returnezeaza maximul si minimul a doua valori

intregi.

2 6

P14. Functie care interschimba doua variabile.

- 136 -

#include <iostream>using namespace std;

struct alte_doua_numere{ int min,max; };alte_doua_numere sp(int,int);int main(){ alte_doua_numere a; a=sp(6,2); cout << a.min << " " <<a.max;}alte_doua_numere sp(int a, int b){ alte_doua_numere T; T.min=(a<b)?a:b; T.max= (a+b)-T.min; return T;}

Introducere in ANSI C++

a=2

b=5

5 2

9.7 Variabile locale si globale. Durata de viata si domeniu

de vizibilitate

Durata de viata a unei variabile reprezinta timpul cat timp aceasta este alocata in memorie, iar domeniul de vizibilitate este zona in care putem utiliza variabila respectiva.

Definirea variabilelor in cadrul blocurilor va face ca variabilele sa fie locale acelui bloc. Chiar daca putem defini variabile in orice bloc, este consacrat ca variabilele locale sa fie asociate cu blocurile functiilor.

Am vazut ca o variabila locala este alocata pe stiva, apare la intrarea in bloc (functie) si dispare la iesirea din bloc (functie). Deci durata de viata a unei variabile locale este din momentul declaratiei si pana la sfarsitul blocului. Domeniul de vizibilitate este dat de blocul variabilei, mai putin alte blocuri interioare in care declaram o variabila cu acelasi nume.

P15. Variabile locale (fara folosirea functiilor).

- 137 -

#include <iostream>using namespace std;

void interschimbare(int& , int&);

int main(){ int a,b; cout << "a="; cin >>a; cout << "b="; cin >>b; interschimbare(a,b); cout << a << " " << b;}

void interschimbare(int& a, int& b){ int t; t=a;a=b;b=t;}

Introducere in ANSI C++

5 12

5 100

11 6

Variabilele globale sunt variabile declarate in afara oricarui bloc. Durata de viata este din momentul declararii si pana la incheierea programului. Domeniu de vizibilitate este intreg programul, mai putin blocurile in care a fost definita o variabila locala cu acelasi nume. Totusi, chiar si in cazul in care variabila globala este "acoperita" de catre una locala cu acelasi nume, putem folosi operatorul scope, ::, pentru a accesa variabila globala din cadrul zonei respective.

In acest caz var va fi variabila locala, iar ::var cea globala.

In general nu este recomandata folosirea variabilelor globale, posibilitatea modificarii acestora din cadrul oricarei functii putand duce la erori logice (bugs) greu de depanat.

- 138 -

#include <iostream>using namespace std;

int main(){ int a=11,b=12; { int a=5; cout << a << " " <<b << endl;

// a din bloc si b din blocul main { int b=100; cout << a << " " << b << endl;

// a din blocul exterior si b din blocul interior } }

{ int b=6; cout << a << " " << b << endl;

// b din bloc si a din blocul main } }

Introducere in ANSI C++

P16. Variabile locale si variabile globale

5 2

2 100

Folosirea variabilelor globale nu este recomandata, accesibilitatea acesteia din orice functie putand duce la erori logice greu de depanat. Urmatoarea problema ilustreaza un astfel de bug datorat folosirii variabilei globale in locul unora locale. Desi nu vom avea erori de compilare, modificarea variabilei contor din main in cadrul functiei apelate va duce la o bucla infinita.

P17. La afisarea unei piramide de numere, folosirea variabielor globale poate

duce la erori logice.

Varianta 1 – in cazul folosirii variabilelor locale

- 139 -

#include <iostream>using namespace std;

int a=2; // variabila globala

void f(){ int a=5; // locala in f cout << a << " " << ::a << endl;}

int main(){ int b=100; // locala in main f(); cout << a << " " << b << endl; }

Introducere in ANSI C++

1

21

321

4321

Varianta 2 –in cazul folosirii variabilelor globale

1

1

- 140 -

#include <iostream>using namespace std;

// int i; // i declarat global

void Descrescator(int);

int main(){ int i; // i declarat local for(i=1;i<=4;i++){ Descrescator(i); cout << endl; }}

void Descrescator(int n){ int i; // i declarat local for(i=n;i>=1;i--) cout << i;}

#include <iostream>using namespace std;

int i; // i declarat global

void Descrescator(int);

int main(){// int i; // i declarat local for(i=1;i<=4;i++){ Descrescator(i); cout << endl; }}

void Descrescator(int n){// int i; // i declarat local for(i=n;i>=1;i--) cout << i;}

Introducere in ANSI C++

.... (se intra in bucla infinita)

9.8 Supradefinirea functiilor

C++ permite folosirea a mai multe functii cu acelasi nume. Diferentierea dintre functii va fi facuta dupa tipul si/sau numarul parametrilor.

P18. Calculati max(a,b,c), folosind max(a,b).

5

P19. Alt exemplu de supradefinire a functiilor

- 141 -

#include <iostream>using namespace std;

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

int main(){ cout << max(2,5,1);}

int max(int a, int b, int c){ return max(max(a,b),c);}

int max(int a, int b){ return (a>b)?a:b;}

Introducere in ANSI C++

123 este un intreg

3.14 este un real

e este un caracter

In cazul in care am fi avut apeluri ca in exemplul de mai jos

am fi obtinut erori de compilare de forma:

p19.cpp: In function ‘int main()’:

p19.cpp:14: error: call of overloaded ‘este(double)’ is ambiguous

p19.cpp:6: note: candidates are: void este(int)

p19.cpp:7: note:                 void este(float)

p19.cpp:8: note:                 void este(char)

- 142 -

#include <iostream>using namespace std;

void este(int);void este(float);void este(char);

int main(){ este(int(123)); este(float(3.14)); este(char('e'));}

void este(int n){ cout << n << " este un intreg" << endl;}

void este(float n){ cout << n << " este un real" << endl;}

void este(char n){ cout << n << " este un caracter" << endl;}

...........int main(){ este(123); este(3.14); este('e');}...........

Introducere in ANSI C++

Deci apelurile in cazul functiilor supradefinite nu au voie sa introduca ambiguitati, functia care va fi apelata fiind unic determinata in momentul apelului.

9.9 Functii cu parametri impliciti

In cazul in care apelul unei functii se face de obicei cu aceleasi valori, putem folosi parametri impliciti pentru acea functie. In acest caz, valorile parametrilor lipsa vor fi date de valorile implicite.

Valorile implicite vor fi specificate in prototip, sub forma:

tip_ret nume_functie(param_neinitializati, param_initializati);

sau:

tip_ret nume_functie(param_initializati);

In momentul apelului, putem omite parametri cu valori implicite, de la dreapta la stanga.

P20. Functie cu parametri impliciti care afiseaza un interval, cu un pas dat.

1 3 5 7

1 2 3 4 5 6 7 8

- 143 -

#include <iostream>using namespace std;

void afisare(int a,int b,int pas=1);

int main(){ afisare(1,8,2); afisare(1,8);// in acest caz ultimul parametru ca fi cel implicit}

void afisare(int a,int b,int pas){ int i; for(i=a;i<=b;i+=pas) cout << i << " "; cout << endl;}

Introducere in ANSI C++

P21. Functie cu parametri impliciti care calculeaza minimul a maxim patru

valori.

5

8

In cazul in care functia ajutatoare ce calculeaza maximul a doua valori ar fi avut numele max (supradefinind functia cu parametri impliciti), ar fi existat o ambiguitate si am fi avut o eroare de forma:

p21.cpp: In function ‘int main()’:

p21.cpp:10: error: call of overloaded ‘max(int, int)’ is ambiguous

p21.cpp:6: note: candidates are: int max(int, int)

p21.cpp:7: note:                 int max(int, int, int, int)

9.10 Parametri vectori

Prototipul ca fi de forma:

tip_returnat nume_functie(tip [], alti_parametri);

Apelul:

nume_functie(Vector, alti_parametri);

De obicei numarul efectiv de valori din vector va fi transmis printr-un alt

- 144 -

#include <iostream>#include <values.h>using namespace std;

int mx(int, int);int max(int a=-MAXINT,int b=-MAXINT,int c=-MAXINT,int d=-MAXINT);

int main(){ cout << max(5,2) << endl; cout << max(1,5,8,3) << endl;}

int mx(int a,int b){ return (a>b)?a:b;}int max(int a, int b, int c, int d){ return mx(mx(a,b),mx(c,d));}

Introducere in ANSI C++

parametru.

Parametri nu se aloca pe stiva ca o variabila simpla, deci un vector de lungime 100 nu va ocupa din stiva 100*sizeof(tip) octeti. Transmiterea vectorului va a vea un comportament analog transmiterii prin referinta, modificarile facute pe elementele vectorului in cadrul functiei fiind regasite in vectorul cu care s-a apelat functia.

P22. Functii care realizeaza urmatoarele operatii pe vectori:

• initializare cu valori aleatoare

• citire

• afisare

• suma elementelor

- 145 -

Introducere in ANSI C++

n=3

83 86 77

n=4

1 2 3 4

10

- 146 -

#include <iostream>using namespace std;

void initializare(int [], int &);void citire(int [], int &);void afisare(int [], int);long suma(int [], int);

int main(){ int a[100],n; // vectorul a de lungime efectiva n int b[10],m; initializare(a,n); afisare(a,n); citire(b,m); cout << suma(b,m);}

void citire(int a[], int &n){ int i; cout << "\nn="; cin >> n; for(i=0;i<n;i++) cin >> a[i];}

void initializare(int a[], int &n){ int i; cout << "\nn="; cin >> n; for(i=0;i<n;i++) a[i]=random()%100;}

void afisare(int a[], int n){ int i; cout << endl; for(i=0;i<n;i++) cout << a[i] << " ";}

long suma(int a[], int n){ int i,S; for(S=i=0;i<n;i++) S+=a[i]; return S;}

Introducere in ANSI C++

P23. Fie doi vectori reprezentand multimi de valori intregi. Realizati functii care

fac: intersectia, reuniunea, diferenta

- 147 -

#include <iostream>using namespace std;void afisare(int [],int);void intersectia(int [], int, int [], int, int [], int&);void reuniunea(int [], int, int [], int, int [], int&);void diferenta(int [], int, int [], int, int [], int&);bool este(int [],int,int);// verifica daca o valoare este sau nu intr-o multime

int main(){ int a[]={2,4,8},n=3; // prima multime int b[]={1,4,8,14},m=4; // a doua multime int c[10],l;// multimea in care tine rezultatul operatiilor intersectia(a,n,b,m,c,l); afisare(c,l);

reuniunea(a,n,b,m,c,l); afisare(c,l); diferenta(a,n,b,m,c,l); afisare(c,l);}bool este(int a[],int n,int val){ int i; for(i=0;i<n;i++) if (a[i]==val) return true; return false;}void intersectia(int a[], int n, int b[], int m, int c[], int&l){ int i; l=0; for(i=0;i<n;i++) if (este(b,m,a[i])) c[l++]=a[i];}void reuniunea(int a[], int n, int b[], int m, int c[], int&l){ int i; for(i=0;i<n;i++) c[i]=a[i]; // completam prima multime l=n; for(i=0;i<m;i++) if (!este(a,n,b[i])) c[l++]=b[i];}void diferenta(int a[], int n, int b[], int m, int c[], int&l){ int i; l=0; for(i=0;i<n;i++) if (!este(b,m,a[i])) c[l++]=a[i]; }void afisare(int a[], int n){ cout << endl; for(int i=0;i<n;i++) cout << a[i] << " ";}

Introducere in ANSI C++

4 8

2 4 8 1 14

9.11 Probleme propuse

Ppx. Reluati toate problemele rezolvate de pana cum, si, acolo unde este posibil, refaceti rezolvarile folosind functii.

- 148 -

Introducere in ANSI C++

Capitolul 10 – Recursivitate

Numim functie recursiva o functie care se autoapeleaza (direct sau indirect).

10.1 Recursivitatea directa

In cazul in care functia contine in corpul ei un apel la ea insasi vorbim despre recursivitate directa.

In practica, forma generala a unei functii recursive va fi urmatoarea:

tip_returnat nume_functie(lista_parametri_formali){

if (!conditie_de_oprire){

...... nume_functie(lista_parametri_actuali)

}

Recursivitatea poate fi utilizat pentru a rezolva elegant multe probleme, dară func iile recursive sunt adesea mai lente decât corespondentele lor nerecursive:ţ

- la fiecare apel depun în stiv valorile parametrilor (dac exist ) i adresa deă ă ă ş

revenire (Cap. 9)

- complexitatea algoritmilor recursivi este de obicei mai mare decat a

"fratilor" iterativi.

P1. Sa se realizeze o functie recursiva ce calculeaza n!

Plecam de la formula recursiva de calcul

>−⋅=

=0,)!1(

0,1!

nnn

nn .

Rescriind formula intr-o varianta mai apropiata de implementarea C/C++, avem:

- 149 -

Introducere in ANSI C++

>−=

=0),1(*

0,1)(

nnfactn

nnfact

Trecerea la urm torul program este naturala:ă

4

4!=24

La orice func ie recursiv trebuie precizat o condi ie de ie ire. În problemaţ ă ă ţ ş factorialului, condi ia de ie ire este 0! care, prin defini ie, este 1. Dac parametrulţ ş ţ ă

primit de fact este 0, atunci func ia returneaz 1, altfel returneaz rezultatulţ ă ă înmul irii dintre valoarea parametrului i factorialul apelat cu valoareaţ ş parametrului minus 1.

Pentru fact(3) avem urmatoarea succesiune de operatii:

(1) calculeaza 3*fact(2), ceea ce duce la primul apel recursiv

(2) calculeaza 2*fact(1) – apel recursiv

(3) calculeaza 1*fact(0) – apel recursiv

(4) returneaza 1 – revenire din fact(0)

(5) returneaza 1 – revenire din fact(1)

(6) returneaza 2 – revenire din fact(2)

(7) returneaza 6 – revenire din fact(3)

- 150 -

#include <iostream>using namespace std;

long fact(int);

int main(){ int n; cin >> n; cout << n << "!=" << fact(n);}

long fact(int n){ if(n==0) return 1; //conditia de oprire return n*fact(n-1); //apel recursiv: n!=n(n-1)!}

Introducere in ANSI C++

Figura 10.1 ilustreaza succesiune de apeluri recursive:

Figura 10.1

P2. Realizati o functie recursiva care calculeaza 2n.

- 151 -

long fact(3){

if(3==0) return 1;

return 3*fact(2);

} 3 nvf

long fact(2){

if(2==0) return 1;

return 2*fact(1);

}3 n

vf 2 n

long fact(1){

if(1==0) return 1;

return 1*fact(0);

}

long fact(0){

if(0==0) return 1;

return n*fact(n-1);

}

Intrarea in recursivitate

3 nvf 2 n

1 n

3 nvf 2 n

1 n0 n

Iesi

rea

in r

ecur

sivi

tate

Introducere in ANSI C++

3

2^3=8

P3. Calculati fibo(n) intr-o functie recursiva (cel de-al n-lea termen din sirul

lui Fibonacci). Cate apeluri recursive au avut loc?

6

fibo(6)=8

Nr. apeluri:15

- 152 -

#include <iostream>using namespace std;

long doila(int);

int main(){ int n; cin >> n; cout << "2^"<< n << "=" << doila(n);}

long doila(int n){ if(n==0) return 1; return 2*doila(n-1); }

#include <iostream>using namespace std;

long fibo(int);int nr_apeluri; // retinem numarul de apeluri ale functiei

int main(){ int n; cin >> n; nr_apeluri=0; cout << "fibo("<< n << ")=" << fibo(n) << endl; cout << "Nr. apeluri:" << nr_apeluri;}

long fibo(int n){ nr_apeluri++; if(n<=2) return 1; return fibo(n-1)+fibo(n-2); }

Introducere in ANSI C++

Folosirea variabilei globale a permis contorizarea simpla a intrarilor in functia recursiva. Numarul mare de apeluri recursive, numar care creste exponential, face ca aceasta implementare sa nu fie practica.

Urmatoarea aplicatie va folosi faptul ca variabilele locale sunt alocate pe stiva, iar prin adaugarea de valori apoi scoaterea lor din stiva, acestea isi inverseaza ordinea.

P4. Inversarea unui text citit de la tastatura folosind o functie recursiva.

abcd

dcba

- 153 -

#include <iostream>using namespace std;

void invers();

int main(){ invers();}

void invers(){ char c; c=getchar(); if (c!='\n') invers(); cout << c;}

Introducere in ANSI C++

10.2 Recursivitatea indirecta

In cazul in care apelul recursiv se face prin intermediul unei alte functii, numim acest tip de apel recursivitate indirecta.

P5. Calculati:

f x , y = x , pentru x≥yg x , y−1 , pentru xy

unde

gx ,y = xy−5, pentru xy≥52∗ f 0, y , pentru xy5

19

- 154 -

#include <iostream>using namespace std;

int f(int,int);int g(int,int);

int main(){ cout << f(5,20);}

int f(int x,int y){ if (x>=y) return x; else return g(x,y-1);}

int g(int x,int y){ if ((x+y)>=5) return x+y-5; else return 2*f(0,y);}

Introducere in ANSI C++

10.3 Probleme propuse

Pp1. Realizati o functie recursiva pentru a afisa primii n termeni ai sirului A, definit ca:

A[n]=3*A[n-1]+2*A[n-2], pentru n>=2

A[0]=A[1]=1

Pp2. Functie recursiva care afiseaza cifrele unui numar.

Pp3. Functie recursiva care descompune un numar in factori primi.

Pp4. Fie un vector de numere intregi pozitive definit global. Realizati o functie recursiva care calculeaza cmmdc-ul elementelor din vector.

Pp5. Scrieti functii recursive pentru a afisa urmatoare piramida de numere:

1

1 2

1 2 3

..........

Pp6. Fie n numar natural citit de la tastatura. Afisati f(n) si g(n), pentru f si g definite:

f(0)=f(1)=1

g(0)=2, g(1)=1

g(n+2)=5*f(n)-g(n)

f(n+1)=g(n+1)-f(n-1)

- 155 -

Introducere in ANSI C++

Capitolul 11 – Fisiere

Pana acum am citit datele de intrare de la tastatura, iar cele de iesire au fost afisate pe ecran. Tastatura si ecranul reprezinta dispozitivele standard de intrare, respectiv iesire.

Figura 11.1

11.1 Redirectarea intrarii/iesirii

Probabil cea mai simpla posibilitate de a citi date dintr-un fisier, respectiv de a scrie datele de iesire in altul, este modificare intrarii si iesirii standard. In acest caz, cin si cout, vor accesa de fapt fisierele care au devenit intrari si iesiri standard.

Vom avea ceva de forma:

Figura 11.2

Functia care permite redirectarea stdin si stdout este freopen.

Pentru redirectarea intrarii avem:

freopen(nume_fisier, "r",stdin);

Dupa executarea functiei, intrarea standard va fi nume_fisier.

Pentru redirectarea iesirii avem:

freopen(nume_fisier, "w",stdout);

Dupa executarea functiei, iesirea standard va fi nume_fisier.

- 156 -

Algoritm

stdin stdout

Algoritm

stdin stdout

3 59 12

Suma:29

Introducere in ANSI C++

P1. Fisierul "intrare.txt" contine numere 2 numere intregi. Scrieti in fisierul

"iesire.txt" suma acestor doua numere.

intrare.txt: 2 5

iesire.txt: 7

P2. Se citeste de la tastatura numele unui fisier care contine 10 numere intregi.

Afisati pe ecran suma lor.

Dati fisierul de intrare:intrare.p02

55

- 157 -

#include <iostream>using namespace std;

int main(){ int a,b; freopen("intrare.txt","r",stdin); freopen("iesire.txt","w",stdout); cin >> a >> b; cout << a+b;}

#include <iostream>using namespace std;

int main(){ int x,i,S; char FileName[20]; cout << "Dati fisierul de intrare:"; cin >> FileName; freopen(FileName,"r",stdin); S=0; for(i=1;i<=10;i++){ cin >> x; S += x; } cout << S;}

Introducere in ANSI C++

11.2 Fluxuri de intrare/iesire

C++ pune la dispozitie fluxurile fisier ifstream (pentru date de intrare) si ofstream (iesire). ifstream si ofstream sunt clase definite in biblioteca fstream si permit declararea unor variabile care vor fi asociate cu fisierele cu care dorim sa lucram. Declaratiilor vor fi de forma:

ifstream flux_intrare; sau:

ifstream flux_intrare(nume_fisier);

respectiv:

ofstream flux_iesire; sau:

ofstream flux_iesire(nume_fisier);

In cazul in care se specifica un nume de fisier, acel fisier va fi deschis in mod automat si va fi accesibil prin intermediul variabilei flux_iesire sau flux_intrare.

Daca nu, sau daca dorim, la nu moment dat sa schimbat fiserul asociat cu o anumita variabila flux, folosim apeluri de forma:

flux.open(nume_fisier);

In acest moment, daca fluxul este de intrare, fisierul este deschis si este pozitionat la inceput, iar daca este de iesire, fiserul este deschis si eventualele date existente sunt sterse.

Accesul la fluxuri de va face analog lucrului cu cin si cout, folosind operatorii >> si <<:

flux_intrare >> variabila;

flux_iesire << expresie;

Citirea unei variabile va fi incheiata la intalnirea unui caracter alb (spatiu, enter, tab), iar caracterele albe dintre doua variabile consecutive citite vor fi ignorate.

Daca dorim citirea unui sir de caractere, inclusiv spatii, vom folosi:

flux_intrare.getline(sir, nr);

Mai sus sir va fi sirul de caractere in care va fi realizata citirea, iar nr reprezinta numarul maxim de caractere care va fi citit.

- 158 -

Introducere in ANSI C++

P3. Fisierul "intrare.txt" contine numere 2 numere intregi. Scrieti in fisierul

"iesire.txt" suma acestor doua numere. (identic cu P1, dar rezolvare cu fluxuri)

intrare.txt: 2 5

iesire.txt: 7

P4. Fisierul "intrare.p04" contine 5 nume de persoane, date pe cate un rand sub

forma nume prenume. Afisati pe ecran persoanele care au numele de patru

caractere.

intrare.p04: Voinea Mircea

 Tica Silviu

 Toth Haralambie

 Ion Ion

 Gheorghe Gheorghe

ecran:

Tica Silviu

- 159 -

#include <fstream>using namespace std;

int main(){ int a,b; ifstream fin("intrare.txt"); ofstream fout("iesire.txt"); fin >> a >> b; fout << a+b;}

#include <fstream>#include <iostream>using namespace std;

int main(){ int i; char nume[20], prenume[20]; ifstream f("intrare.p04"); for(i=1;i<=5;i++){ f >> nume >> prenume; if (strlen(nume)==4) cout << nume << " " << prenume << endl; } }

Introducere in ANSI C++

Toth Haralambie

11.3 Citirea unui numar cunoscut de valori dintr-un fisier

La multe probleme, enuntul va da ca fiserul de intrare un fisier in care, ca prima valoare se va da numarul de valori care vor fi citite ulterior din fisier. In acest caz, avem urmatoarea abordare:

P5. Fisierul "numere.p05" contine, pe primul rand un numar intreg n, iar pe

urmatorul separat de cate un spatiu, n numere reale. Scrieti numere in alt fisier, in

ordine crescatoare, cate 5 pe un rand.

numere.p05

8

1 2 3 4 1 2 3 4

iesire.p05

1 2 3 4 1 

2 3 4 

- 160 -

flux_intrare >> Nr_valori;

for(i=1;i<=Nr_valori;i++){

flux_intrare >> X;

foloseste(X);

}

#include <fstream>using namespace std;

int main(){ int i,n,x; ifstream fin("numere.p05"); ofstream fout("iesire.p05"); fin >> n; for(i=1;i<=n;i++){ fin >> x; fout << x << " "; if (i%5==0) fout << endl; } }

Introducere in ANSI C++

P6. Fisierul "matrice.txt" contine, pe primul rand un numar intreg n, iar pe

urmatorele n randuri cate n numere intregi care vor forma informatii dintr-o

matrice scrisa pe linii. Afisati pe ecran matricea.

matrice.txt

3

1 2 3

4 5 6

7 8 9

ecran

1 2 3

4 5 6

7 8 9

11.4 Citirea pana la sfarsitul fisierului

Daca fluxul a ajuns la sfarsit sau daca a avut loc o eroare la citirea unei valori din flux, atunci o instructiunea flux_intrare >> var; returneaza NULL. Folosindu-ne de aceasta vom avea:

- 161 -

#include <fstream>#include <iostream>using namespace std;

int main(){ int i,j,n,x,a[10][10]; ifstream f("matrice.txt"); f >> n; for(i=0;i<n;i++) for(j=0;j<n;j++) f >> a[i][j]; for(i=0;i<n;i++){ for(j=0;j<n;j++) cout << a[i][j] << " "; cout << endl; } }

while (flux_intrare >> X)

foloseste(X);

Introducere in ANSI C++

O alta varianta implica testarea sfarsitului de fisier folosint functia:

flux_intrare.eof()

Aceasta functie va returna true daca s-a ajuns la sfarsitul fluxului si false in caz contrar. Vom avea:

P7. Se citeste numele unui fisier text de la tastatura. Realizati o copie a acestuia

in fiserul "copie.txt".

P8. Fie fisierul "intrare.p08". Scrieti in fisierul "frecv.p08" caracterele care

apar in fiserul de intrare si de cate ori apar ele.

- 162 -

#include <fstream>#include <iostream>using namespace std;

int main(){ ifstream f; ofstream g("copie.txt"); char FileName[20],linie[250]; cout << "Dati numele fisierului:"; cin >> FileName; f.open(FileName); while(!f.eof()){ f.getline(linie,250); g << linie << endl; }}

while (!flux_intrare.eof()){

flux_intrare >> X;

foloseste(X);

}

Introducere in ANSI C++

intrare.p08

abc abc

abc dede

ffffffffff

frecv.p08

  2

a 3

b 3

c 3

d 2

e 2

f 10

- 163 -

#include <fstream>using namespace std;

int main(){ ifstream f("intrare.p08"); ofstream g("frecv.p08"); char S[100]; int ct[256],i; for(i=0;i<256;i++) ct[i]=0; while(!f.eof()){ f.getline(S,100); for(i=0;S[i];i++) ct[S[i]]++; } for(i=0;i<256;i++) if (ct[i]) g << char(i) << " " << ct[i] << endl;}

Introducere in ANSI C++

11.5 Probleme propuse

Pp1. Un fisier contine nu text in limba engleza. Contorizati literele mici care apar in text, apoi afisati literele mici din ext, in ordine crescatoare dupa frecvente.

Pp2. Verificati daca n fisiere citite de la tastatura sunt identice ca si continut.

Pp3. Numarati cuvintele dintr-un fiser text. Separatorii dintre cuvinte sunt Enter, ‘,’ si ‘.’.

Pp4. Un fisier contine numere intregi. Scrieti numerele pare intr-un fiser si cele impare in altul.

Pp5. Cititi o matrice dintr-un fisier. Scrieti inversa matricii in alt fisier.

Pp6. Inversati liniile unui fisier text al carui nume este citit de la tastatura, scriind rezultatul in alt fisier.

- 164 -

Introducere in ANSI C++

Capitolul 12 – Pointeri

Accesul la o locatie de memorie folosita de program este realizata printr-o adresa unica. Un pointer este o variabila care contine o adresa de memorie.

12.1 Declaratie si operatii elementare

La declararea unei variabile, acesteia i se asociaza o locatie de memorie. Determinarea adresei la care se gaseste o variabila se face folosind operatorul &.

&variabila = "adresa la vare este memorata variabila"

In cazul in care avem o constructie de forma &variabila, acesta poate insemna:

– o referinta, daca apare in context de declaratie de variabila

– adresa unei variabile, daca este o instructiune (nu o declaratie)

Pentru a putea lucra direct cu adresele de memorie declaram pointeri:

tip *pointer1, *pointer2, ..., pointern;

unde tip este un tip de data oarecare (int, float, tip definit de utilizator, etc.).

Pentru a utiliza valoarea care este memorata intr-o locatie indicata de un pointer, se foloseste operatorul de redirectare *.

Semnul * poate sa insemne:

– declaratie de pointer, daca apare in context de declaratie

– valoarea care se gaseste la adresa indicata de un pointer, in context de instructiune

Prin simpla declaratie a unui pointer, valoarea acestuia nu este initializata, pointerul indicand o zona de memorie oarecare. Acest tip de pointer se numeste pointer liber, iar folosirea acestuia inainte de initializare poate avea rezultate dezastruoase (nu poate folosi nimanui modificarea unei zone oarecare de memorie!).

- 165 -

Introducere in ANSI C++

In cazul in care dorim sa specificam ca un pointer nu indica o adresa valida vom folosi constanta NULL.

p=NULL; // initializarea pointerului nul

Conditia de testarea daca un pointer este valid va fi:

if (p!=NULL) ... sau, echivalent:

if (p) ...

Observam ca pointerii liberi vor aparea ca pointeri valizi!

Exista posibilitatea de a initializa un pointer la declarare, cu NULL sau cu adresa unei variabile anterior declarate:

int *p=NULL;

int *q=&var;  

P1. Operatii elementare cu pointeri

0x8048906

0xbff9eefc 0xbff9eef8

0xbff9ef04 0xbff9ef04   0xbff9ef00 0xbff9ef00

11 11   3 3

La rulari succesive adresele afisate pot sa difere.

- 166 -

#include <iostream>using namespace std;

int main(){ int a=1,b=2; // declararea variabilelor intregi a si b int &copie_a = a; // copie_a este un alias al lui a int *p,*q; // declararea pointerilor la intregi p si q cout << p << endl;// afisarea unui pointer liber!!! p=&a; // p contine adresa lui a *p=*p+10; // a=a+10 q=&b; // q contine adresa lui b ++(*q); // ++b;

cout << &p << " " << &q << endl; cout << &a << " " << p << " " << &b << " " << q<< endl; cout << a << " " << *p << " " << b << " " << *q; }

Introducere in ANSI C++

Starea memoriei in cazul problemei P1 poate fi urmatoarea:

1. Dupa declaratii:

Figura 12.1

Observam ca p indica o adresa oarecare din memorie, variabilele a si b sunt initializate, a si copie_a sunt de fapt una si aceeasi variabila.

2. Dupa atribuiri:

Figura 12.2

In acest moment, pointerii p si q vor avea valori valide, modificarea locatiilor de memorie indicate ducand la modificarea valorii variabilelor a si b.

Desi pointerii se comporta similar unor variabile obisnuite (au o adresa asociata in memorie, contin valori etc.), in urmatoarele figuri in care vor mai aparea pointeri, acestia vor fi desenati ca in Figura 12.3, pentru a creste claritatea.

Figura 12.3

Incercarea de a asocia pointeri de un anumit tip cu adresele unor variabile de tipuri diferite va duce la erori de compilare.

- 167 -

...0xbff9ef00 0xbff9ef04 3 11...0xbff9eef8 0xbff9eefc 0xbff9ef00 0xbff9ef04

q p b acopie_a

...3 11

q p

b acopie_a

...

...? 0x8048906 2 1...0xbff9eef8 0xbff9eefc 0xbff9ef00 0xbff9ef04

q p b acopie_a

Introducere in ANSI C++

P2. Tipul indicat de un pointer

In cazul in care vom scoate comentariile, ultimelPointeri void si utilizarea lore doua atribuiri vor da urmatoarele erori:

p02.cpp: In function ‘int main()’:

p02.cpp:13: error: cannot convert ‘double*’ to ‘int*’ in assignment

p02.cpp:14: error: cannot convert ‘int*’ to ‘double*’ in assignment

O categorie aparte de pointeri sunt pointerii void:

void *pointer;

La prima vedere, acest tip de pointer pare sa aiba un grad de utilizare redus. Cu toate acestea, in combinatie cu operatorul de conversie explicita de tip (cast), ofera o flexibilitate crescuta aplicatiilor noastre. Acest tip de pointer va putea indica zone de memorie de dimensiune variabila (dimensiune data in cazul altor pointeri de tipul de data indicat), ceea ce va duce la urmatoarea problema: dereferentierea nu va putea fi facuta direct, fiind obligatoriei folosirea operatorului cast pentru a specifica tipul indicat in acel moment de pointerul void.

P3. Pointerii void si modul lor de utilizare

- 168 -

#include <iostream>using namespace std;

int main(){ int a,*p; // intreg si pointer la int double b,*q; // intreg si pointer la double p=&a; q=&b; // p=&b; // aceste atribuiri nu sunt valide// q=&a; }

Introducere in ANSI C++

1 12.34 a

2 37.02 b

Vom explica mai departe urmatoarea expresie: (*(int *)p)++.

Pointerul p este de tip void, deci ca prim pas trebuie sa specificam tipul spre care pointeaza, in cazul nostru int:

(int *)p

Pentru a accesa zona de memorie vom dereferentia pointerul de mai sus:

*(int *)p sau *((int *)p)

La sfarsit, aplicam operatorul de incrementare, efectul fiind incrementarea intregului indicat de pointerul p:

(*(int *)p)++

O situatie aparte o reprezinta pointerii la pointeri, urmatoarea aplicatiei ilustrand un astefel de caz.

- 169 -

#include <iostream>using namespace std;

int main(){ int i=1; double d=12.34; char c='a'; void *p; cout << i << " " << d << " " << c << endl; p=&i; (*(int *)p)++; p=&d; (*(double *)p) = (*(double *)p)*3; p=&c; (*(char *)p) = 'b' ; cout << i << " " << d << " " << c;}

Introducere in ANSI C++

P4. Pointeri la pointeri.

123 123

Situatia din problema anterioara este descrisa in Figura 12.4.

Figura 12.4

12.2 Aritmetica pointerilor

In subcapitolul anterior am folosit operatori de referentiere/dereferentiere pentru a lucra cu pointeri. In C++ exista o serie de alte operatii specifice lucrului cu variabile de tip pointer.

12.2.1 Atribuirea

Operatorul = se comporta analog atribuirii dintre doua variabile oarecare. O Expresie de forma:

p1 = p2 = ... = pn

va face ca pointerii p1, p2, ... , pn sa indice toti locatia de memorie indicata initial de pn.

- 170 -

#include <iostream>using namespace std;

int main(){ int i=123; int *p; // pointer la int int **q; // pointer la pinter la int int ***t; // pointer la pointer la pointer la int :)

p=&i; q=&p; t=&q; cout << i << " " <<***t;}

123i

pqt

Introducere in ANSI C++

Figura 12.5

Pointerii care apar in expresia de atribuire trebuie sa fie de acelasi tip. O situatie aparte apare in cazul in care utilizam pointeri void, acestia putand retine valoarea oricaror pointeri, indiferent de tipul lor de baza.

P5. Atribuirea pointerilor

123 123 123 123 123

12.2.2 Operatorii relationali

Operatorii ==, !=, < , >, <=, >= pot fi folositi cu operanzi pointeri. Primii doi operatori verifica daca doi pointeri indica sau nu aceeasi locatie de memorie, iar ultimii verifica daca un pointer indica o locatie de memorie mai mica sau mai mare fata de alt pointer. In general expresii relationale apar intre pointeri care indica acelasi obiect in memorie (vezi 12.5).

P6. Afisati locatia cea mai mica si locatia cea mai mare de memorie alocate in

- 171 -

123

pnp1 p2 ...

??123

pnp1 p2 ...

#include <iostream>using namespace std;

int main(){ int i=123; int *p1,*p2,*p3; double *p4; void *pv; p3=&i; p1=p2=p3; pv=p1; // atribuire corecta// p4=p1; // eroare la compilare (pointerii nu au acelasi tip) cout << i << " " << *p1 << " " << *p2 << " "; cout << *p3 << " " << *((int *)pv); }

Introducere in ANSI C++

program.

0xbff6f618 0xbff6f614 0xbff6f610 0xbff6f60c 0xbff6f608 0xbff6f604

0xbff6f604 0xbff6f614

Observatii:

– in program am alocat variabile de 4 tipuri diferite, deci e obligatorie folosirea pointerilor void

– pointerii sunt alocati in memorie ca orice alta variabila; adresa cea mai mica este data de pointerul in care retinem adresa cea mai mare :)

- 172 -

#include <iostream>using namespace std;

int main(){ double a; int b; int *q; void *p, *pmic, *pmare; cout << &a << " "<< &b << " "<< &q << " "; cout << &p << " "<< &pmic << " "<< &pmare << endl; pmic=pmare=&a; q=&b; if (pmic<q) pmic=q; // comparatie dintre doi pointer de tipuri diferite if (pmare>=q) pmare=q; p=&q; pmic=(p<pmic)?p:pmic; pmare=(p>pmare)?p:pmare; // comparatie dintre pointeri de acelasi tip p=&p; pmic=(p<pmic)?p:pmic; pmare=(p>pmare)?p:pmare; // p indica p !!! pmic=(&pmic<pmic)?p:pmic; pmare=(&pmic>pmare)?&pmic:pmare; // comparatie dintre un pointer si o referentiere p=&pmare; pmic=(p<pmic)?p:pmic; pmare=(p>pmare)?p:pmare; cout << pmic << " " << pmare; // if (p>0x10000); // comparatie dintre un pointer si un intreg (eroare) }

Introducere in ANSI C++

– putem compara direct un pointer si adresa unei variabile oarecare

12.2.3 Scaderea si adunarea cu un intreg

Operatiile de scadere si de adunare dintre un pointer vor "deplasa" pointerul in memorie inapoi sau inainte fata de pozitia initiala, in functie de tipul de baza al pointerului. Acest tip de operatii nu vor putea avea ca operanzi pointeri void.

tip *p;

p++ ; // p indica urmatoarea locatie de memorie de tipul dat

p­­;  // p indica locatia de memorie anterioara de tipul dat

p = p + i; // p "sare" spre dreapta in memorie cu i pozitii 

q = p ­ i; // q indica la stanga lui p cu i pozitii 

Practic, valoarea efectiva cu care se modifica pointerul la aceste operatii este un multiplu de sizeof(tip). Este permisa folosirea formelor scurte de atribuire += si ­=.

P7. Adunarea si scaderea dintre un pointer si un intreg

0xbfcb90ac 0xbfcb90a8

0xbfcb90b0 0xbfcb90d0

0xbfcb90ac 0xbfcb90a8

- 173 -

#include <iostream>using namespace std;

int main(){ int a,b; int *p=&a,*q=&b; cout << p << " " << q << endl; p++; q=q+10; cout << p << " " << q << endl; --p; q-=10; cout << p << " " << q << endl; }

Introducere in ANSI C++

Pentru exemple utile vezi 12.5.

12.2.4 Diferenta a doi pointeri

Daca p si q sunt doi pointeri cu acelasi tip de baza (diferit de void) operatia:

p­q

returneaza un intreg si va da numarul de obiecte de tipul indicat de p si q care se gasesc intre cei doi pointeri (vezi 12.5 pentru exemple).

12.3 Alocarea dinamica a memoriei

Pana in acest moment, toate programele noastre au folosit variabile alocate static. Variabilele globale sunt alocate in momentul compilarii si cele locale sunt alocate pe stiva la rulare, in ambele cazuri cunoscandu-se exact cantitatea de memorie necesara. In situatiile in care cantitatea de memorie necesara alocarii datelor cu care lucram nu este cunoscuta decat in timpul rularii se folosesc functii de alocare/dealocare dinamica a memoriei.

C++ ofera operatorii new si delete pentru a aloca dinamic memoria.

new tip - aloca o zona de memorie de dimensiune sizeof(tip) si returneaza un pointer la zona alocata, respectiv NULL daca nu s-a putut realiza alocarea

delete p  - dealoca o zona de memorie indicata de un pointer, de dimensiunea tipului de baza a pointerului p

In general, alocarea dinamica trebuie facuta intr-o constructie de forma:

if (p=new tip){

foloseste p

delete p;

}

else 

trateaza eroarea la alocare

- 174 -

Introducere in ANSI C++

P8. Alocarea dinamica a memoriei

2

3

2+3=5

Pentru a aloca/dealoca mai multe obiecte de acelasi tip, se pot folosi urmatoarele forme ale operatorilor new si delete (vezi 12.5 pentru exemple):

p = new tip[nr] - aloca o zona de memorie de dimensiune sizeof(tip)*nr si returneaza un pointer la zona alocata, respectiv NULL daca nu s-a putut realiza alocarea

delete [] p  - dealoca zona de memorie alocata anterior

12.4 Pointeri la structuri

O situatie relativ speciala este cazul in care tipul de baza al unui pointer este o structura. In acest caz este introdus operatorul ­>, urmatoarele doua notatii fiind echivalente pentru p un pointer la o structura:

(*p).membru

p­>membru

P9. Folosirea pointerilor la structuri.

- 175 -

#include <iostream>using namespace std;

int main(){ int *p,*q; if ((p= new int) && (q= new int)){ cin >> *p >> *q; cout << *p << "+" << *q << "=" << *p+*q; delete p; delete q; } else cout << "Eroare la alocare!";}

Introducere in ANSI C++

z= 2 ­1*i

12.5 Legatura dintre pointeri si tablouri

12.5.1 Accesul la elementele unui tablou folosind pointeri

In C si in C++ numele unui tablou este un pointer constant la primul element al tabloului. Deci, in cazul declaratiei:

int A[10];

A este un pointer la primul intreg din vector. Faptul ca A este un pointer constant implica faptul ca acesta nu va putea fi modificat in program. Figura 12.6 ilustreaza o situatie posibila in memorie in cazul declaratiei anterioare.

Figura 12.6

A fiind un pointer, *A va reprezenta valoarea primului element, notatiile A[0] si *A fiind echivalente. Folosind aritmetica pointerilor si faptul ca A este pointer la primul element, accesul la elementele tabloului va putea fi realizat intr-o varietate de forme, ca in Figura 12.7:

- 176 -

#include <iostream>using namespace std;

struct complex{ float re,im;};

int main(){ complex z,*p; z.re=2; z.im=-1; p=&z; cout << "z= " << (*p).re << char((p->im<0)?' ':'+');

cout << p->im << "*i";}

? ? ? ? ? ?...

A

10 intregi

Introducere in ANSI C++

Figura 12.7

P10. Diferite modalitati de acces la elementele unui tablou

A:11 12 13 14 15 16 

Parcurgerea unui tablou A, de lungime n, se poate face fara a folosi notatia indexata folosind urmatoarea schema generala:

O alta variantaimplica pozitionarea unui pointer pe pozitia imediat urmatoare ultimului element din tablou si folosirea acestui pointer la verificarea sfarsitului de vector:

P11. Impementati functiile de initializare random, de citire si de afisare a

vectorilor fara a folosi notatia indexata.

- 177 -

? ? ? ? ? ?...

*A

10 intregi

A[1] *(A+2) *(3+A) 4[A]

#include <iostream>using namespace std;

int main(){ int A[10]={11,12,13,14,15,16}; int *p; p=A+5; cout << "A:" << *A << " " << A[1] << " "; cout << *(A+2) << " " << *(3+A) << " "; cout << 4[A] << " " << *p << " "; }

for(p=A;p­A<n;p++)foloseste(*p);

sf=A+n; // sau sf=&A[n];for(p=A;p<sf;p++)

foloseste(*p);

Introducere in ANSI C++

n=3

10 20 30

3 6 7 5 3

10 20 30

- 178 -

#include <iostream>using namespace std;

void init(int [], int);void citeste(int [], int &);void afiseaza(int [], int);

int main(){ int A[10],n; int B[10],m; n=5; init(A,n); citeste(B,m); afiseaza(A,n); afiseaza(B,m);}

void init(int A[], int n){ int *p; for(p=A;p-A<n;p++) *p=rand()%10;}

void citeste(int A[], int &n){ int *p, *sf; cout << "n="; cin >> n; sf=A+n; for(p=A;p<sf;p++) cin >> *p;}

void afiseaza(int A[], int n){ int i; for(i=0;i<n;i++) cout << *(A+i) << " "; cout << endl;}

Introducere in ANSI C++

12.5.2 Alocarea dinamica a tablourilor

Pnetru a aloca un vector abia dupa ce cunoastem numarul exact de elemente, folosim urmatoarea schema generala, A fiind un pointer la tip:

Accesul la elementele vectorului poate fi facut indexat sau folosind pointeri.

P12. Fie un vector alocati dinamic si initializat cu valorile 0 si 1. Mutati valorile

de 0 la inceput si cele de 1 la sfarsitul vectorului.

- 179 -

cin >> n;A= new tip[n];foloseste(A,n);delete []A;

Introducere in ANSI C++

n=10

1 0 1 1 1 1 0 0 1 1

0 0 0 1 1 1 1 1 1 1

- 180 -

#include <iostream>using namespace std;

void init(int [], int);void rezolva(int [], int &);void afiseaza(int [], int);

int main(){ int *A,n; cout << "n="; cin >> n; A=new int[n]; init(A,n); afiseaza(A,n); rezolva(A,n); afiseaza(A,n); delete []A;}

void init(int A[], int n){ int i; for(i=0;i<n;i++) A[i]=rand()%2;}

void rezolva(int A[], int &n){ int *st, *dr; st=A; dr=A+n-1; while(st<dr){ while ((st<dr)&&(*st==0)) st++; while ((st<dr)&&(*dr==1)) dr--; if (st<dr){*st=0; *dr=1;} }}

void afiseaza(int A[], int n){ int i; for(i=0;i<n;i++) cout << A[i] << " "; cout << endl;}

Introducere in ANSI C++

12.5.3 Vectori de pointeri

Declararea unui vector de pointeri se face similar declaratiei unui vector de un tip oarecare:

tip *Vector[N];

unde N este o constanta de tip intreg.

In acest caz, Vector[0], Vector[1],... Vector[n­1] vor fi pointeri spre tip.

Fie urmatoarea declaratie de matrice:

int A[10][10];

Pe exemplul dat, A[0] va fi un pointer la primul element al vectorului ce reprezinta prima linie a matricii. Analog, A[i] este un pointer spre linia i a matricii.

Figura 12.8

P13. Fie o matrice patratica de intregi initializata random. Sortati crescator

fiecare linie a matricii.

- 181 -

A[0][0] A[0][1] A[0][2]

A[1][0] A[1][1] A[1][2]

. . .

. . .

A[0][9]

A[1][9]

A[9][0] A[9][1] A[9][2] . . . A[9][9]

. . .

. . .

. . .

. . .

A[0]

A[1]

A[9]

Introducere in ANSI C++

3 6 7 5

3 5 6 2

9 1 2 7

0 9 3 6

3 5 6 7

2 3 5 6

1 2 7 9

0 3 6 9

- 182 -

#include <iostream>using namespace std;

int init(int [10][10], int &);int afis(int [][10], int);int sort(int [], int);

int main(){ int A[10][10],n,i; init(A,n); afis(A,n); for(i=0;i<n;i++) sort(A[i],n); cout << endl; afis(A,n);}int init(int A[10][10], int &n){ int i,j; n=4; for(i=0;i<n;i++) for(j=0;j<n;j++) A[i][j]=rand()%10;}int afis(int A[][10], int n){ int i,j; for(i=0;i<n;i++){ cout << endl; for(j=0;j<n;j++) cout << A[i][j] << " "; }}int sort(int A[], int n){ int i,j,t; for(i=0;i<n-1;i++) for(j=i+1;j<n;j++) if (A[i]>A[j]){ t=A[i]; A[i]=A[j]; A[j]=t; }}

Introducere in ANSI C++

Un caz in care folosirea vectorilor de pointeri se dovedeste utila este cazul in care lucram cu matrici de caractere.

P14. Fie o matrice de caractere initializata cu zilele saptamanii. Afisati zilele in

ordine lexicografica.

luni marti miercuri joi vineri sambata duminica

duminica joi luni marti miercuri sambata vineri

- 183 -

#include <iostream>#include <string>using namespace std;

void afis(char [7][10]);void inter(char *, char *);void sort(char [7][10]);

int main(){ char Zi[7][10]={"luni", "marti", "miercuri", "joi", "vineri", "sambata", "duminica"};

afis(Zi); sort(Zi); afis(Zi); }

void afis(char Zi[7][10]){ for(int i=0;i<7;i++) cout << Zi[i] << " "; cout << endl;}

void inter(char *s1, char *s2){ char t[10]; strcpy(t,s1); strcpy(s1,s2); strcpy(s2,t);}

void sort(char Zi[7][10]){ int i,j; char t[10]; for(i=0;i<6;i++) for(j=i+1;j<7;j++) if (strcmp(Zi[i],Zi[j])>0) inter(Zi[i], Zi[j]);}

Introducere in ANSI C++

12.6 Legatura dintre pointeri si functii

In C/C++ avem posibilitatea de a transmite parametri de tip pointer unei functii, de a returna pointeri din functie si de a defini un pointer la o functie. Acesta ultima facilitate va permite apelul functiei folosind pointerul si ofera posibilitatea de a transmite parametri de tip functie.

12.6.1 Functii cu parametri pointeri

Prototipul unei functii care are ca parametri pointeri este de forma:

tip_returnat nume_functie(..., tip*, ...);

La apel, parametrul efectiv corespunzator parametrului formal pointer trebuie sa fie un pointer care acelasi tip de baza, mai putin situatia in care tip este void. Parametrul pointer se comporta ca orice alt tip de parametru. El va fi alocat pe stiva (e variabila locala), dar in general utilizarea pointerului in cadrul functiei se va face in varianta dereferentiata. Folosirea pointerilor sau a referintelor, in cazul in care dorim modificarea unor parametri in cadrul unei functii va conduce la un rezultat similar. Urmatoarele doua definitii de functii sunt echivalente:

tip_returnat nume_functie(..., tip *p, ...){

foloseste(*p);

}

si

tip_returnat nume_functie(..., tip &p, ...){

foloseste(p);

}

In cazul in care se doreste modificare pointerului (nu a valorii indicate de catre acesta) se va folosi o referinta la pointer:

tip_returnat nume_functie(..., tip*&, ...);

P15. Functie care realizeaza interschimarea a doua variabile de tip intreg,

folosind parametri pointeri.

- 184 -

Introducere in ANSI C++

1 2

2 1

Observam ca apelul trebuie facut cu adresele variabilelor de interschimbat!

P16. Functie care interschimba doi pointeri la int.

1 2

2 1

Daca nu am fi folosit referinte la pointeri interschimarea ar fi avut loc doar in

- 185 -

#include <iostream>using namespace std;

void inter(int *, int *);

int main(){ int x=1,y=2; cout << x << " " << y << endl; inter(&x,&y); cout << x << " " << y << endl;}

void inter(int *a, int *b){ int t; t=*a; *a=*b, *b=t;}

#include <iostream>using namespace std;

void inter(int *&, int *&);

int main(){ int x=1,y=2; int *p=&x, *q=&y; cout << *p << " " << *q << endl; inter(p,q); cout << *p << " " << *q << endl;}

void inter(int *&a, int *&b){ int *t; t=a; a=b; b=t;}

Introducere in ANSI C++

cadrul functiei. Urmatoarea varianta este echivalenta.

P17. Functie care interschimba doi pointeri la int (varianta 2).

1 2

2 1

Un caz de transmitere de pointer a fost discutat anterior, in 9.10. Am stabilit ca numele unui vector este un pointer la primul sau element, deci tranmiterea acestui nume este un caz particular e tranmitere de parametru pointer.

P18. Realizati o functie care, primind ca parametri doi vectori alocati static si

lungimile acestora, realizeaza interschimbarea lor (ca si continut).

- 186 -

#include <iostream>using namespace std;

void inter(int **, int **);

int main(){ int x=1,y=2; int *p=&x, *q=&y; cout << *p << " " << *q << endl; inter(&p,&q); cout << *p << " " << *q << endl;}

void inter(int **a, int **b){ int *t; t=*a; *a=*b; *b=t;}

Introducere in ANSI C++

A:1 2 3 4

B:10 20 30

A:10 20 30

B:1 2 3 4

In problema anterioara, simpla interschimbare a pointerilor reprezentand numele vectorilor este imposibila, acestia fiind pointeri constanti.

P19. Realizati o functie care, primind ca parametri doi vectori alocati dinamic si

lungimile acestora, realizeaza interschimbarea lor (ca si continut).

- 187 -

#include <iostream>using namespace std;

void inter(int [], int &, int [], int *);void copiaza(int [], int &, int [], int);void afis(int [], int );

int main(){ int A[10]={1,2,3,4},n=4, B[10]={10,20,30},m=3; cout << "A:"; afis(A,n); cout << "B:"; afis(B,m); inter(A,n,B,&m); cout << "A:"; afis(A,n); cout << "B:"; afis(B,m); }

void inter(int A[], int &n, int B[], int *m){ int T[100],l; copiaza(T,l,A,n); copiaza(A,n,B,*m); copiaza(B,*m,T,l); }

void copiaza(int A[], int &n, int B[], int m){ n=m; for(int i=0;i<n;i++) A[i]=B[i];}

void afis(int A[], int n){ for(int i=0;i<n;i++) cout << A[i] << " "; cout << endl;}

Introducere in ANSI C++

A:1 2 3 4

B:10 20 30

A:10 20 30

B:1 2 3 4

In acest caz inteschimbarea are loc mult mai simplu, nefiind necesare mutarea in memorie a elementelor tablourilor, simpla interschimbare a pointerilor reprezentand vectorii si a lungimii acestora fiind suficienta. Din nou este de remarcat folosirea referintelor la pointeri!

12.6.2 Functii care returneaza pointeri

Prototipul unei functii care returneaza pointeri este de forma urmatoare:

tip * nume_functie(lista_parametri);

- 188 -

#include <iostream>using namespace std;

void inter(int *&, int &, int *&, int &);void afis(int [], int );

int main(){ int *A=new int[4],n=4, *B=new int[3],m=3; for(int i=0;i<n;i++) A[i]=i+1; for(int i=0;i<m;i++) B[i]=(i+1)*10; cout << "A:"; afis(A,n); cout << "B:"; afis(B,m); inter(A,n,B,m); cout << "A:"; afis(A,n); cout << "B:"; afis(B,m); }

void inter(int *&A, int &n, int *&B, int &m){ int *T,t; t=n; n=m; m=t; T=A; A=B; B=T;}

void afis(int A[], int n){ for(int i=0;i<n;i++) cout << A[i] << " "; cout << endl;}

Introducere in ANSI C++

O astfel de functie poate fi apelata din orice loc al programului in care are sens sa apara un pointer care nu se va modifica.

P20. Functii de alocare/dealocare dinamica a vectorilor.

5

1 2 3 4 5

A:1 2 3 4 5

P21. Afisati prima linie dintr-o matrice patratica ce contine cel putin o valoare

nula (se stie ca matricea contine cel putin un zero).

- 189 -

#include <iostream>using namespace std;

void afis(int [], int);int * creare(int &);void stergere(int *,int);

int main(){ int *A,n; A=creare(n); cout << "A:"; afis(A,n); stergere(A,n);}

void afis(int A[], int n){ for(int i=0;i<n;i++) cout << A[i] << " "; cout << endl;}

int * creare(int &n){ int *T; cin >> n; if (!(T= new int[n])){ cout << "Eroare la alocare!"; exit(1); } for(int i=0;i<n;i++) cin >> T[i]; return T;}

void stergere(int *A, int n){ if (A) delete []A;}

Introducere in ANSI C++

1 2 0 3 

In aceasta problema, o functie (afis), primeste ca parametru un pointer rezultat un urma apelului unei alte functii (prima).

12.6.3 Pointeri la functii

O facilitate foarte puternica oferita de C/C++ este posibilitatea definirii si folosirii pointerilor la functii. In plus, numele unei functii (fara paranteze si parametri) este un pointer. Acest pointer va putea fi transmis ca parametru, deci o functie va putea primi ca parametru o alta functie!

Forma generala a unei declaratii de pointer la functie:

tip_returnat (* nume_pointer) (lista_parametri);

Parantezele care cuprind * si numele pointerului nu sunt optionale, acestea facand diferentierea dintre o declaratie de pointer la functie si un prototip de functie care returneaza un pointer.

- 190 -

#include <iostream>using namespace std;

void afis(int *, int);int * prima(int [4][4],int );

int main(){ int A[4][4]={{1,2,3,4}, {2,3,4,5}, {1,2,0,3}, {0,1,0,2}},n=4; afis(prima(A,n),n); }

void afis(int A[], int n){ for(int i=0;i<n;i++) cout << A[i] << " ";}

int * prima(int A[4][4],int n){ int i,j; for(i=0;i<n;i++) for(j=0;j<n;j++) if (A[i][j]==0) return A[i];}

Introducere in ANSI C++

P22. Apelul unei functii folosind un pointer la functie.

20 30 

P23. Functie are primeste ca parametru alta functie.

sin(0)=0

- 191 -

#include <iostream>using namespace std;

int dublu(int);int triplu(int);

int main(){ int (* p)(int); // declaratie pointer la functie p=dublu; cout << p(10) << " "; p=triplu; cout << p(10);

int dublu(int x){return 2*x;}int triplu(int x){return 3*x;}

#include <iostream>#include <cmath>using namespace std;

double calcul(double,double);void afisare(char *, double (* )(double), double);void afisare(char *, double (* )(double,double), double,double);

int main(){ afisare("sin",sin,0); afisare("pow",pow,2,3); afisare("functia mea", calcul, 2,3); }

double calcul(double x, double y){return x*x+y*y;}

void afisare(char *text, double (* p)(double), double x){ cout << endl << text << "(" << x << ")="; cout << p(x);}

void afisare(char *text, double (* p)(double,double), double x, double y){ cout << endl << text << "(" << x << "," << y<< ")="; cout << p(x,y);}

Introducere in ANSI C++

pow(2,3)=8

functia mea(2,3)=13

P24. Folosirea unui vector de pointeri la functii.

x     sin    cos   tan   abs

0      0     1     0     0

10  ­0.54 ­0.84  0.65    10

20   0.91  0.41   2.2    20

30  ­0.99  0.15  ­6.4    30 

12.6.4 Functii cu numar variabil de parametri

In anumite situatii, numarul si tipul parametrilor ai unei functii este cunoscut inainte de apelul efectiv. In acest caz, folosim functii cu numar variabil de parametri:

tip_returnat nume(lista_parametri,...);

unde lista_parametri contine cel putin un parametru, iar cele trei puncte "anunta" compilatorul ca s-ar putea sa mai apara si alti parametri.

Compilatorul nu poate face nici un fel de verificare asupra parametrilor

- 192 -

#include <iostream>#include <cmath>using namespace std;

typedef double (* Tip_pointer_functie)(double);

int main(){ int i,x; Tip_pointer_functie p[4]={sin,cos,tan,abs}; cout << "x sin cos tan abs\n"; for(x=0;x<=30;x+=10){ cout << x << " "; for(i=0;i<4;i++){ cout.width(6); cout.precision(2); cout << p[i](x); } cout << endl; }}

Introducere in ANSI C++

nespecificati explicit si accesul la acestia trebuie facut "de mana", in functie de informatii auxiliare oferite in cadrul parametrilor clar specificati (vezi situatia functiei printf din cstdio).

Pentru a vedea cum anume putem accesa parametri nespecificati, tinem cont de urmatoarele:

– parametri se aloca pe stiva

– parametri se aloca in ordine inversa a scrierii lor in lista de parametri

– stiva creste in memorie de sus in jos

Figura 12.9 ilustreaza un posibil caz de apel:

Figura 12.9

Conform observatiilor anterioare, situatie memorie este:

Figura 12.10

Observam ca parametri necunoscuti se gasesc la adrese imediat urmatoare ultimului parametru cunoscut, deci, daca se poate determina la rulare numarul si tipul parametrilor necunoscuti (prin informatii date de parametri cunoscuti), acestia vor putea fi accesati folosind pointeri.

P25. Functie cu numar variabil de parametri care calculaeza o expresie de

forma: c1 op1 c2 op2 c3 ... opn cn+1, unde ci sunt intregi si opi este + sau -.

- 193 -

p1p2

pn......

parametricunoscuti

...

...

... parametrinecunoscuti

Stiva

p1 p2 pn ... ...

parametricunoscuti

parametrinecunoscuti

Introducere in ANSI C++

10

­15

11403253

Observatii:

– pozitionarea pe primul parametru necunoscut se poate face si mai simplu

– compilatorul nu poate verifica daca functia a fost corect apelata, dupa cum se vede la ultimul apel, unde lipseste un parametru

– in acest caz folosim o valoare oarecare, un interg care este peste varful stivei!

- 194 -

#include <iostream>using namespace std;

int calcul(char *, ...);

int main(){ cout << calcul("+++",1,2,3,4) << endl; cout << calcul("-+-+",-1,2,-3,4,-5) << endl; cout << calcul("+",1); // apel incorect, imposibil de detectat de catre compilator }

int calcul(char *op, ...){ int E,i; char **t,*s; int *p; // pointer prin care accesam operanzii t=&op; t++; p=(int *) t; // pozitionam p pe primul parametru necunoscut E=*p; p++; // initializam E si mutam p pe al doiela param. nec. for(s=op;*s;s++,p++) // ne "deplasam" in paralel pe

//operanzi si pe operatori if (*s=='+') E+=*p; else E-=*p; return E;}

Introducere in ANSI C++

Un caz particular de functie cu numar variabil de parametri este cazul in care ultimul parametru cunoscut este un intreg care specifica numarul de parametri necunoscuti care urmeaza, paramentri nocunoscuti fiind de tip int. Forma generala pentru de lucru cu astfel de functie este:

In acest caz, parametri sunt dati practic de elementele vectorului p: p[1], p[2], ...,p[nr].

P26. Functie cu numar necunoscut de parametri care realizeaza suma valorilor

(primul parametru da numarul de parametri necunoscuti).

10

­3

- 195 -

tip_returnat nume(int nr, ...){tip *p=&nr;for(int i=1;i<=nr;i++)

foloseste(p[i]);}

#include <iostream>using namespace std;

int suma(int, ...);

int main(){ cout << suma(4,1,2,3,4) << endl; cout << suma(5,-1,2,-3,4,-5) << endl; }

int suma(int nr, ...){ int S,*p,i; p=&nr; S=0; for(i=1;i<=nr;i++) S+=p[i]; return S;}

Introducere in ANSI C++

12.7 Probleme propuse

Pp1. Rescrieti functiile de la problemele rezolvate din capitolele cu vectori si cu siruri de caractere folosind pointeri (fara acces indexat).

Pp2. Realizati o functie cu numar variabil de parametri care calculeaza cmmdc-ul parametrilor.

Pp3. Realizati o functie care primeste ca parametri:

– o functie de forma int f(int)

– un vector de intregi

– lungimea vectorului

si returneaza valoare minima atinsa de functia f pentru parametri fiind valorile din vector.

- 196 -

Introducere in ANSI C++

Anexe

A1. Shortcut-uri uzuale folosite in mediul Borland C++ 3.1

Shortcut Efect

F1 - help

^F1 - help context sensitive - va da informatii despre cuvantul pe care se gaseste in acel moment cursorul de editare

F2 - save - la prima salvare se deschide fereastra save as...

F3  - open - permite deschiderea unei text sursa salvat anterior

alt+F3  - close - inchide fereastra curenta

F5  - maximizare fereastra curenta

alt+F5 - afisarea ferestrei output- in acest mod putem vizualiza rezultatele afisate pe ecran

F6 - navigare intre ferestre

F10 - intrare in meniuri- navigarea prin meniuri se face cu cursorii si tasta Enter pentru selectare

^F9 - rularea programului

Tabelul A1 – Shortcut-uri generale

- 197 -

Introducere in ANSI C++

Shortcut Efect

Shift + Cursori - selectare text

^Insert - copy- este copiata in clipboard zona selectata

Shift + Insert - paste- copiere din clipboard la pozitia cursorului

^Delete - stergerea zonei selectate

Alt+Backspace - undo- se revine cu un pas inapoi in procesul de editare- Atentie: la salvare se sterge istoricul Undo

Shift+Alt+Backspace - redo- se merge cu un pas inainte in procesul de editare- este activat doar dupa operatii Undo

Tabelul A2 - Scurtaturi legate de editor

A2 Erori uzuale si cauze posibile (Borland C++ 3.1)

Eroare Cauza posibila

Declaration terminated incorrectly

';' dupa main(), inainte de '{'

Declaration syntax error lipseste ';' dupa o declaratie

Statement missing ; lipseste ';' cu un rand mai sus

- 198 -

Introducere in ANSI C++

Eroare Cauza posibila

Undefined symbol _main in module c0.ASM

s-a gresit numele functiei main (de exemplu s-a scris mein)

Undefines symbol 'cout' s-a uitat #include <iostream.h>

Illegal structure operation schimbati '<<' cu '>>' sau invers

Function 'cutarescu' should have a prototype

lipseste o directiva #include sau am gresit numele functiei (de exemplu crlscr in loc de clrscr)

Undefined symbol 'a' trebuie declarata variabila

Possibly incorrect assignment  intr-o conditie s-a pus = in loc de == (este de fapt un warning, dar in 99% din cazuri programul nu va functiona corect)

A3 Indentarea programelor C++

Cand scrii cod, nu conteaza cat de scurt si de simplu ti se pare, scrie-l intr-o maniera care il face usor de citit. Foloseste un standard al indentarii pentru codul tau si nu te abate de la el de-a lungul intregului program.

A3.1. Regulile de baza ale indentarii

De fiecare data indenteaza corpul unei instructiuni cu o distanta constanta fata de primul caracter al instructiunii (prin corp al unei instructiuni intelegem setul de actiuni pe care le controleaza acea instructiune). Instructiunile care au corp includ buclele, instructiunile conditionale si subprogramele.

De exemplu, fie urmatoarea instructiune if-else:

- 199 -

Introducere in ANSI C++

if  (a==0) {

z_aparitii++;printf(“Zero a aparut de %d ori\n”,z_aparitii);

}else {

nz_aparitii++;printf(“valori diferite de zero sunt %d\n”,z_aparitii);

}

Motivul pentru care folosim indentarea devine evident daca studiem alternativa urmatoare:

if  (a==0) {z_aparitii++;printf(“Zero a aparut de %d ori\n”,z_aparitii);} else {nz_aparitii++;printf(“valori diferite de zero sunt %d\n”,z_aparitii);}

Chiar daca vom intelege pana urma ce face sursa data, timpul necesar va fi sensibil mai mare fata de primul caz.

Care este numarul de spatii cu care ar trebui sa indentati corpul unei instructiuni?

In principiu, o indentare eficienta respecta regulile urmatoare:

- cel putin 2 spatii (1 spatiu nu va faca o diferenta notabila)

- nu mai mult de 8 spatii (altfel ramaneti fara spatiu pentru instructiuni)

- aplicand aceeasi indentare in toata sursa

Sugerez folosirea TAB-ului (setat, daca permite editorul, pe 4 spatii), fiind mai usor de folosit.

In cazul functiilor definite de utilizator, respectiv functia main, aplicati regulile de mai sus. De exemplu:

int main(){int a,b; // declaratii de variabilefloat r;printf(“Dati numitorul:”);

- 200 -

Introducere in ANSI C++

scanf(“%d”,&b);. . .

}

Celelalte subprograme vor fi aliniate la functia main. In cazul in care numarul de parametri este mare, este posibil ca antetul functiei sa nu incapa pe in rand. In acest caz, indentarea parametrilor se va face in asa fel incat numele si tipul returnat de functie sa fie usor de cazut. Deci,

int o_functie_oarecare(int indice_de_plecare, int contor_folositor,     arbore *radacina,informatie_utila Info[]);

sau:

int o_functie_oarecare( int indice_de_plecare, int contor_folositor, 

        arbore *radacina,informatie_utila Info[]);

dar nu:

int o_functie_oarecare(int indice_de_plecare, int contor_folositor, arbore *radacina,informatie_utila Info[]);

A3.2 Stiluri de indentare ale blocurilor

Printre multitudinea de variante, s-au remarcat doua:

A3.2.1 Aliniaza “{“ cu “}”

Prin alinierea acoladelor, cititorul nu va avea dubii in legatura cu blocul din care face parte o anumita instructiune. De exemplu:

if  (a==0){

z_aparitii++;printf(“Zero a aparut de %d ori\n”,z_aparitii);

}else 

{

- 201 -

Introducere in ANSI C++

nz_aparitii++;printf(“valori diferite de zero sunt %d\n”,z_aparitii);

}

Un dezavantaj minor al acestei abordari este acela ca numarul de linii ocupat este mare. Programatorilor le place sa vada cat mai mult cod pe ecran la un moment dat, iar aceasta metoda “pierde” linii.

A3.2.2 Aliniaza “}” cu instructiunea ce controleaza blocul

In programare, blocurile au rolul de a grupa instructiuni ce formeaza corpul unei instructiuni. Deci “}” marcheaza, atat sfarsitul blocului cat si sfarsitul corpului instructiunii. Folosind aceasta observatie, devine logica alinierea acoladei inchise la instructiunea a carei corp o inchide. Acolada deschisa va fi amplasata la sfarsitul primei linii a instructiunii.

Exemplu:

if  (a==0)  {z_aparitii++;printf(“Zero a aparut de %d ori\n”,z_aparitii);

} else {

nz_aparitii++;printf(“valori diferite de zero sunt %d\n”,z_aparitii);

}

A3.3 Instructiuni de selectie: if, if-else si switch

A3.3.1 if cu corp simplu

Daca nu avem decat o singura instructiune de executat, nu este necesara prezenta acoladelor. Daca instructiunea este indeajuns de scurta, este acceptabila amplasarea ei pe aceeasi linie cu if-ul. Oricare dintre urmatoarele varianta poate fi considerata o optiune buna:

if (numar < 0) numar = 0;

sau:

if (numar < 0) 

- 202 -

Introducere in ANSI C++

numar = 0;

sau:

if (numar < 0) {numar = 0;

}

Pentru ultima varianta, folosirea acoladelor, chiar daca nu este neaparat necesara, poate duce la cresterea lizibilitatii sursei.

A3.3.2 if cu corp compusif (contor > 0) {

printf(“Numar pozitiv la orizont!”);contor­­;

}

A3.3.3 if-else cu corp simplu

Exact ca in cazul if-ului simplu, folosirea acoladelor nu este necesara, dar nici nu deranjeaza.

if (numar > 0)printf(“Sunt pozitiv!”);

else printf(“Sunt negativ sau zero!”);

sau:

if (numar > 0) {printf(“Sunt pozitiv!”);

} else {printf(“Sunt negativ sau zero!”);

}

A3.3.4 if-else cu corp compus

Evident, in acest caz acoladele sunt necesare. O varianta posibila ar fi cea data in sectiunea (2.2). Repet, alegeti o varianta care vi se potriveste, apoi folositi-o de fiecare data.

A3.3.5 if-else in combinatia corp simplu si corp compus

Daca ati ales varianta in care folositi acoladele chiar si pentru corpuri simple, acesta este unul din cazurile in care isi dovedeste utilitatea. Aveti de ales intre:

- 203 -

Introducere in ANSI C++

if (numitor < 0) {printf(“Numitor negativ!”);numitor = ­ numitor;

}else

printf(“Totul e OK”);

si:

if (numitor < 0) {printf(“Numitor negativ!”);numitor = ­ numitor;

}else {

printf(“Totul e OK”);}

A3.3.6 Instructiunea switch

Se poate pleca de la premiza ca instructiunea switch are corpuri multiple, daca ne gandim ca fiecare case este inceputul unui alt bloc de cod. Cele doua variante consacrate ar fi:

switch (litera) {case ‘ ‘:  printf(“Spatiu \n”);

break;case ‘a’:case ‘e’:case ‘i’:case ‘o’:case ‘u’: printf(“Vocala \n”);

break;default: printf(“Nimic interesant.”);}

respectiv:

switch (litera) {case ‘ ‘: printf(“Spatiu \n”);

break;case ‘a’:case ‘e’:case ‘i’:case ‘o’:case ‘u’: printf(“Vocala \n”);

break;default: printf(“Nimic interesant.”);

- 204 -

Introducere in ANSI C++

}

A3.4 Instructiuni repetitive: while, for si do-while

A3.4.1 while fara corp

Desi pare ciudat pentru un incepator, instructiuni repetitive fara corp (mai ales for) se regasesc in C++ mai des decat ar parea la prima vedere. Urmatoarea secventa de program, pozitioneaza indicele din vector pe prima valoare pozitiva:

i=0;while ( (vector[i++]<0) && (i<numar_elemente) ) ;

Pentru a nu ignora punct-virgula de la sfarsitul randului (si deci a avea impresia ca instructiunea ce urmeaza while-ului face parte din corpul instructiunii), se recomanda variantele:

i=0;while ( (vector[i++]<0) && (i<numar_elemente) ) 

;

sau:

i=0;

while ( (vector[i++]<0) && (i<numar_elemente) ) {;

}

O alta posibilitate ar fi cea de a insera un comentariu prin care se indica blocul vid:

i=0;while ( (vector[i++]<0) && (i<numar_elemente) ) {

/* sunt un bloc vid */;}

A3.4.2 while cu corp simplu

Observatiile de la sectiunea anterioara raman valabile. Deci, daca corpul este destul de mic, avem urmatoarele situatii:

- 205 -

Introducere in ANSI C++

i = 0;while ((i<numar_elemente) && (vector[i] != valoare_cautata)) i++;

i = 0;while ((i<numar_elemente) && (vector[i] != valoare_cautata)) 

i++;

i = 0;while ((i<numar_elemente) && (vector[i] != valoare_cautata)) {

i++;}

A3.4.3 while cu corp compus

Nu exista variante altele decat cele discutate in (3).

i=1;while ( i < Maxim ) {

printf(“%d” , i);i=i*2;

}

A3.4.4 for in toate variantele

Nimic nou sub soare; respectati regulile de la while.

A3.4.5 do-while fara corp sau cu corp simplu

Avem variantele:

i = ­1;do i++; while ((i< numar_elemente)&&(a[i]<0));

sau:

i = ­1;do 

i++;while ((i< numar_elemente)&&(a[i]<0));

sau:

i = ­1;do {

i++; } while ((i< numar_elemente)&&(a[i]<0));

- 206 -

Introducere in ANSI C++

Este de preferat ultima varianta, while la inceput de rand putand duce la confuzii.

A3.4.6 do-while cu corp compus

Nimic nou de remarcat.

A3.5 Indentarea instructiunilor imbricate

Indentarea este cu atat mai importanta in cazul in care avem instructiuni complexe ce fac parte din alte instructiuni complexe. Fara imbricare, vom putea usor considera o instructiune ca finnd in afara unei bucle, desi ea face parte din corpul ei.

Considerand fiecare TAB de la inceput de rand ca fiind nivelul indentarii, putem avea urmatoarea situatie:

[0] int main (void)[0] {[1]        int   i;[1]        int   suma = 0 , numar;

[1]        printf("\nIntroduceti cinci numare intregi pozitive\n");

[1]        for (i=1; i<=5; i++) {[2]        scanf("%d",&numar);[2]        if (numar>0) [3] suma += numar;[2]        else {[3] suma ­= numar;

[1]        }[1]        printf("\nSuma este:%d\n”,suma );[0] }

Se poate enunta urmatoarea regula: “fiecare corp se indenteaza cu un nivel in plus”.

Un caz special il reprezinta instructiunile if-else imbricate. Daca numarul de imbricari nu este mare, nu avem nici o problema. In caz contrar ne trezim in situatia de a nu mai avea spatiu in partea dreapta a randului. Urmatorul exemplu da o posibila solutie:

 if (nota >= 9)

- 207 -

Introducere in ANSI C++

printf(“Foarte Bine”);else if (nota >= 8)

printf(“Bine”);else if (score >= 7)

printf(“OK”);else if (score >= 6)

printf(“Satisfacator”);else if (score >= 5)

printf(“La limita”);else

printf(“Nasol”);

A4 Precedenta operatorilor

Nivel Operator Descriere Grupare

1 :: scope Left­to­right

2

() [] . ­> ++ ­­ dynamic_cast static_cast reinterpret_cast const_cast typeid

postfixLeft­to­right

3

++ ­­ ~ ! sizeof new delete

unary (prefix)

* &indirection and reference (pointers)

+ ­ unary sign operator

Right­to­left

4 (type) type castingRight­to­left

5 .* ­>* pointer­to­member Left­to­right

6 * / % multiplicativeLeft­to­right

7 + ­ additive Left­to­right

8 << >> shiftLeft­to­right

- 208 -

Introducere in ANSI C++

9 < > <= >= relationalLeft­to­right

10 == != equality Left­to­right

11 & bitwise ANDLeft­to­right

12 ^ bitwise XOR Left­to­right

13 | bitwise ORLeft­to­right

14 && logical AND Left­to­right

15 || logical ORLeft­to­right

16 ?: conditional Right­to­left

17= *= /= %= += ­= >>= <<= &= ^= != assignment

Right­to­left

18 , comma Left­to­right

A5 Cuvintele cheie

Lista cu cuvintele cheia ANSI C++ este:

and, and_eq, asm, auto, bitand, bitor, bool, break, case, catch, char, class, compl, const, const_cast, continue, default, delete, do, double, dynamic_cast, else, enum, explicit, export, extern, false, float, for, friend, goto, if, inline, int, long, mutable, namespace, new, not, not_eq, operator, or, or_eq, private, protected, public, register, reinterpret_cast, return, short, signed, sizeof, static, static_cast, struct, switch, template, this, throw, true, try, typedef, typeid, typename, union, unsigned, using, virtual, void, volatile, wchar_t, while, xor, xor_eq 

- 209 -

Introducere in ANSI C++

Bibliografie

[1] Kris Jamsa, Lars Klander - „Totul despre C si C++”, editura Teora, 2001

[2] Bjarne Stroustrup – „C++”, editura Teora, 2003

[3] Breazu Macarie – „Programarea Orientata pe Obiecte – Principii”, editura ULBS, 2002

[4] Bogdan Patrut - „Aplicatii in C si C++”, editura Teora, 2001

[5] Dragos Acostachioaie - „Programare C si C++ sub Linux”, Editura Polirom, 2002

[6] Thomas H. Cormen, Charles E. Leiserson, Ronald R.Rovest - „Introducere in algoritmi”, editura Agora, 2000

[7] Carmen Popescu - „Culegere de probleme de informatica”, editura Donaris, 2002

- 210 -

Introducere in ANSI C++

CuprinsIntroducere...............................................................................................................1

Capitolul 1 – Primul program................................................................................21.1 Structura generala a unui program C++.........................................................2 1.2 Hi man!.........................................................................................................31.3 Compilarea si rularea programului................................................................4

1.3.1 In mediul Borland C++ 3.1...................................................................41.3.2 Folosind compilatorul g++....................................................................5

Capitolul 2 – Notiuni de baza.................................................................................62.1 Notiunea de algoritm......................................................................................62.2 Citirea si afisarea datelor...............................................................................72.3 Tipuri de date. Variabile si constante............................................................92.4 Secvente escape............................................................................................142.5 Referinte (alias-uri)......................................................................................162.6 Probleme propuse.........................................................................................17

Capitolul 3 – Expresii C++....................................................................................183.1 Atribuirea.....................................................................................................183.2 Conversii de tip............................................................................................203.3 Expresii aritmetice.......................................................................................233.4 Expresii logice.............................................................................................253.5 Operatorul conditional.................................................................................273.6 Operatorul virgula........................................................................................303.7 Operatori care actioneaza la nivel de bit......................................................313.8 Probleme propuse.........................................................................................32

Capitolul 4 – Instructiuni conditionale................................................................334.1 Instructiunea if.............................................................................................334.2 Instructiunea switch.....................................................................................404.3 Probleme propuse.........................................................................................43

Capitolul 5 – Instructiuni repetitive.....................................................................445.1 Instructiunea while.......................................................................................445.2 Instructiunea do-while.................................................................................475.3 Instructiunea for...........................................................................................49

- 211 -

Introducere in ANSI C++

5.4 break si continue..........................................................................................535.5 instructiunea goto.........................................................................................555.6 Probleme tip.................................................................................................56

5.6.1 Probleme pe cifrele unui numar..........................................................565.6.2 Probleme de divizibilitate....................................................................585.6.3 Sume, produse, numarari.....................................................................605.6.4 Maxime si minime...............................................................................635.6.5 Citirea mai multor numere de la tastatura...........................................645.6.6 Generarea unor serii complexe de numere .........................................66

5.7 Probleme propuse.........................................................................................69

Capitolul 6 – Vectori (matrici)..............................................................................706.1 Declararea si initializarea.............................................................................706.2 Citirea si scrierea vectorilor.........................................................................726.3 Generari de vectori.......................................................................................756.4 Cateva probleme simple...............................................................................776.5 Sortarea si interclasarea...............................................................................816.6 Declararea matricilor...................................................................................856.7 Citirea si afisarea matricilor.........................................................................856.8 Probleme cu matrici.....................................................................................876.9 Probleme propuse.........................................................................................91

Capitolul 7 – Siruri de caractere..........................................................................927.1 Declaratie si initializare...............................................................................927.2 Operatii de citire/scriere pe siruri de caractere............................................947.3 Accesul la caracterele unui sir.....................................................................957.4 Functii specifice sirurilor de caractere.........................................................97

Capitolul 8 – Structuri si alte tipuri utilizator..................................................1078.1 Structuri......................................................................................................1078.2 Uniuni.........................................................................................................1118.3 Campuri de biti..........................................................................................1148.4 Tipul enumerat...........................................................................................1158.5 Probleme propuse.......................................................................................117

Capitolul 9 – Functii............................................................................................1189.1 Generalitati.................................................................................................1189.2 Functii care nu primesc si nu returneaza nimic.........................................119

- 212 -

Introducere in ANSI C++

9.3 Functii care nu returneaza nimic, dar primesc date de intrare...................1219.4 Functii care nu primesc date de intrare dar returneaza o valoare..............1279.5 Functii care primesc un numar oarecare de parametri de intrare si returneaza o singura valoare............................................................................1299.6 Functii ce returneaza mai multe valori......................................................1339.7 Variabile locale si globale. Durata de viata si domeniu de vizibilitate......1379.8 Supradefinirea functiilor............................................................................1419.9 Functii cu parametri impliciti.....................................................................1439.10 Parametri vectori......................................................................................1449.11 Probleme propuse.....................................................................................148

Capitolul 10 – Recursivitate................................................................................14910.1 Recursivitatea directa...............................................................................14910.2 Recursivitatea indirecta............................................................................15410.3 Probleme propuse.....................................................................................155

Capitolul 11 – Fisiere...........................................................................................15611.1 Redirectarea intrarii/iesirii.......................................................................15611.2 Fluxuri de intrare/iesire............................................................................15811.3 Citirea unui numar cunoscut de valori dintr-un fisier..............................16011.4 Citirea pana la sfarsitul fisierului.............................................................16111.5 Probleme propuse.....................................................................................164

Capitolul 12 – Pointeri.........................................................................................16512.1 Declaratie si operatii elementare..............................................................16512.2 Aritmetica pointerilor..............................................................................170

12.2.1 Atribuirea........................................................................................17012.2.2 Operatorii relationali.......................................................................17112.2.3 Scaderea si adunarea cu un intreg...................................................17312.2.4 Diferenta a doi pointeri...................................................................174

12.3 Alocarea dinamica a memoriei................................................................17412.4 Pointeri la structuri...................................................................................17512.5 Legatura dintre pointeri si tablouri..........................................................176

12.5.1 Accesul la elementele unui tablou folosind pointeri.......................17612.5.2 Alocarea dinamica a tablourilor......................................................17912.5.3 Vectori de pointeri...........................................................................181

12.6 Legatura dintre pointeri si functii............................................................184

- 213 -

Introducere in ANSI C++

12.6.1 Functii cu parametri pointeri...........................................................18412.6.2 Functii care returneaza pointeri.......................................................18812.6.3 Pointeri la functii.............................................................................19012.6.4 Functii cu numar variabil de parametri...........................................192

12.7 Probleme propuse.....................................................................................196

Anexe.....................................................................................................................197A1. Shortcut-uri uzuale folosite in mediul Borland C++ 3.1...........................197A2 Erori uzuale si cauze posibile (Borland C++ 3.1)......................................198A3 Indentarea programelor C++......................................................................199

A3.1. Regulile de baza ale indentarii.........................................................199A3.2 Stiluri de indentare ale blocurilor......................................................201A3.3 Instructiuni de selectie: if, if-else si switch.......................................202

A3.4 Instructiuni repetitive: while, for si do-while..................................205 A3.5 Indentarea instructiunilor imbricate...................................................207

A4 Precedenta operatorilor..............................................................................208A5 Cuvintele cheie...........................................................................................209

Bibliografie...........................................................................................................210

- 214 -