Bun venit tuturor incepatorilor si chiar avansatilor in domeniul limbajelor
de programare. t6e7ei
Acesta este episodul pilot al unei serii de articole ce isi propune initierea
in cel mai popular limbaj la ora actuala, C++.
Pentru inceput, vor fi prezentate sintaxa, elemente de baza, apoi, dupa citeva
episoade se va trece la chestii mai avansate, programare orientata pe obiecte, programare
Windows si chiar programare pentru UNIX / LINUX.
Elementul de baza in toate articolele va fi exemplul. Veti gasi de fiecare data
coduri sursa pe care sa le puteti compila si executa.
Elemente da baza, sintaxa, biblioteci standard:
Limbajul C este considerat a fi un limbaj de nivel mediu, spre deosebire de
Pascal sau
Fox care sunt considerate de nivel inalt.
Ce insemna acest lucru? Inseamna ca ofera un control mai strict asupra procesorului, memoriei si a calculatorului in general. Pentru a scrie si executa programe
C aveti nevoie de un compilator (un program ce converteste textul sursa in cod executabil). Pentru
moment ne vom referi la compilatorul Borland C++ 3.1. Structura unui program C++ arata cam
asa:
#include < biblioteca_standard_1.h >
...
#include < biblioteca_standard_n.h > declaratii si definitii de variabile declaratii si definitii de functii functia main - elementul principal din program
o variabila se defineste astfel: tip_variabila nume_variabila a=valoarei;
(elementele scrise intre paranteze patrate sunt optionale) tip_variabila poate
fi: • char: tip intreg cu semn cu valori cuprinse intre -128 si + 127 (pe 8 biti)
• int: tip intreg cu semn cu valori cuprinse intre -32768 si + 32767 (pe
16 biti)
• long: tip intreg cu semn cu valori cuprinse intre - 2147483648 si +
2147483647 (pe 32 biti)
fiecare din tipurile de mai sus poate fi precedat de specificatorul unsigned:
• unsigned char: tip intreg fara semn cu valori cuprinse intre 0 si
+ 255 (pe 8 biti)
• unsigned int: tip intreg fara semn cu valori cuprinse intre 0 si + 65535
(pe 16 biti)
• unsigned long: tip intreg fara semn cu valori cuprinse intre 0 si +
4294967295 (pe 32 biti) alte tipuri:
• float: tip real in virgula mobila pe 32 biti
• double: tip real in virgula mobila pe 64 biti
• long double: tip real in virgula mobila pe 80 biti
Nota: unsigned nu se poate folosi cu nici unul din tipurile reale. In toate compilatoarele
pentru
Windows 32, tipul int este identic cu tipul long (32 de biti). Un echivalent
al 'vechiului' int (pe 16 biti) este tipul short. In anumite compilatoare pentru Windows 32, tipul long
double este identic cu tipul double (Ex. Microsoft Visual C++ 5, 6).
Variabile tablou: tip_var nume_varadimensiunei;
Exemplu: int xa10i; // tablou de zece intregi
O functie (sau procedura) este o secventa de program ce executa o anume actiune,
de obicei generalizata. Poate avea propriile declaratii de variabile in corpul ei, variabile
care ii sunt locale si accesibile doar din interiorul ei. Declaratia unei functii arata astfel: tip_returnat nume_functie(paramatri functie)
A corp_functie
S tip_returnat se refera la tipul de valoare returnat de functie, si poate fi
oricare din tipurile de mai sus, inclusiv de tipul void. Acest tip special indica faptul ca functia nu intoarce
nici un rezultat.
Parametrii functiei sunt declarati astfel: tip_parametru nume_parametru, ...
Specificatorul void indica faptul ca functia nu primeste nici un parametru.
Exemple: int functie(int x, int y)
A return x+y;
S
Este declaratia unei functii ce primeste doi parametri de tip int, si intoarce
un rezultat de tip int
(in cazul de fata, suma parametrilor). x si y se numesc parametri formali ai
functiei si sunt accesibili doar in interiorul corpului functiei A S. void functie(int y)
AS
Este o functie ce primeste un parametru de tip int, dar nu face nimic. O functie
total inutila, bineinteles.
Nota:
Nu se pot declara in program variabile de tip void. void variabila; // Gresit!!!
Exemplu de program:
#include // este o biblioteca standard de functii
// pentru operatii de intrare / iesire (tastatura) void main() // functie fara parametri, void nu este obligatoriu.
A printf("Primul program C++\n");
S
Se afiseaza pe ecran textul dintre ghilimele, de cite ori rulati programul.
Primul program C++
Primul program C++
Primul program C++
...
Ultima parte (\n) nu apare. Este doar un caracter de control ce semnaleaza trecerea
la o linie noua, altfel, textul urmator ar aparea in continuarea: (Primul program
C++ Primul program
...) Exemplul 2:
#include <stdio.h> void main() // functie fara parametri, void nu este obligatoriu.
A int n = 10; printf("n = %d\n", n);
S
Pe ecran apare: n = 10
Explicatii:
%d este un specificator de format, ii spune functiei printf ca trebuie sa afiseze
in locul lui o valoare de tip intreg. Functia printf are un numar variabil de parametri. Vom
vedea mai incolo cum se pot defini astfel de functii. exemplu: printf("Numere: %d %d %d", x, y, 10);
Se vor afisa valorile lui x, y, si apoi valoarea 10.
Bibliotecile Borland C++ contin foarte multe functii (citeva sute), si prezentarea
tuturor nu face subiectul acestor articole. Anumite functii vor fi descrise in momentul
in care sunt utilizate.
Pentru celelalte se recomanda consultarea help-ului.
Instructiune if:
Sintaxa: if (conditie)
A instructiuni_1;
S else
A instructiuni_2;
S
Instructiunea for:
Sintaxa: for (var = valoare_initiala; conditie_de_continuare; instructiune_de_continuare)
A instructiun1;
S
Exemplu: int i; for (i = 0; i < 100; i++) A...S
Se executa secventa A...S cu I luind valori de la 0 la 99.
Nota:
Daca corpul unui for (sau if) contine o singura instructiune, nu mai sunt necesare
acoladele:
Exemplu: for (j = 0; j <= 10; j++) printf("%d ", j);
Efect: 0 1 2 3 4 5 6 7 8 9 10
for (j = 0; j <= 10; j++)
A printf("%d ", j); printf("%d, ", j*j);
S
Efect: 0 0, 1 1, 2 4, 3 9 ...
Va ofer acum doua surse. Trebuiesc compilate si apoi executate.
#include <stdio.h> // Biblioteca standard cu functii de intrare / iesire
#include <conio.h> // Biblioteca standard cu functii pentru lucru cu ecranul
in mod text
int n = 10; // Variabila globala initializata la declaratie
// Variabilele globale ( declarate in afara oricarei functii
// sunt accesibile de oriunde ( dupa declaratie )
void dreptunghi( int x1, int y1, int x2, int y2 )
// Functia primeste parametri 4 intregi si ca efect
// deseneaza cadrul unui dreptunghi pe ecran de coordonate
// x1,y1 si x2,y2.
A int i; // Variabila locala functiei dreptunghi.
// nu este accesibila decit in cadrul functiei dreptunghi.
for ( i=x1; i<=x2; i++ )
A gotoxy( i,y1 ); // functie <conio.h> ce pozitioneaza cursorul
// la pozitia i, y1 printf("Û"); // caracterul 219
gotoxy( i,y2 ); printf("Û");
S
for ( i=y1; i<=y2; i++ )
A gotoxy( x1,i ); // functie <conio.h> ce pozitioneaza cursorul
// la pozitia i, y1 printf("Û");
gotoxy( x2,i ); printf("Û");
S
S
void main()
A clrscr(); // functie <conio.h> ce sterge ecranul in mod text textmode(3);
dreptunghi( 1,1, 79,25 ); dreptunghi( 10,10, 20,20 ); dreptunghi( 12,12, 18,18 ); dreptunghi( 15,15, 60,23 );
S
/*
Se observa ca daca dreptunghiul ce se doreste a fi desenat ajunge in coltul din dreapta jos al ecranului ( 80,25 ), rezultatul nu este cel dorit. Asta din cauza ca la scrierea pe ultima pozitie de pe ecran, functia printf deplaseaza continutul acestuia cu o linie in sus ( exact ca la un teleimprimator ).
*/
#include <stdio.h>
#include <conio.h>
int nra10i; // Numar maxim de numere int n;
void main()
A clrscr();
printf( "Cite numere? " ); scanf( "%d", &n ); // functie <stdio.h> pentru citirea de
la tastatura
// parametrul al doilea, &n, semnifica adresa variabile n
if ( n <= 0 || n > 10 ) // daca n <= 0 SAU n > 10
A printf( "Numar invalid ( 1 - 10 )\n" ); return;
S
for ( int i = 0; i < n; i++ )
A printf( "\nnra%di = ", i ); scanf( "%d", &nraii );
S
int m = n-1;
int sorted = 0;
while ( sorted == 0 ) // atit timp cit sorted este egal de 0
A sorted = 1;
for ( i = 0; i < m; i++ ) if ( nraii > nrai+1i ) // daca nu sunt in ordine, se inverseaza
A int aux = nraii; nraii = nrai+1i; nrai+1i =aux;
sorted = 0;
S
m--; // m se decrementeaza deoarece dupa fiecare parcurgere a
// sirului, pe ultima pozitie se va afla elementul de valoare maxima
S
printf( "\n" ); for ( i = 0; i < n; i++ ) printf( "%d ",nraii );
printf( "\n" );
S
Utilizarea functiilor grafice oferite de
Borland C++ 3.1.
Elementele de grafica prezentate in acest articol nu sunt specifice doar mediului
de dezvoltare BC31 ( Borland C++ 3.1 ), ele fiind pre- zente in orice compilator
C++ ce poate scoate executabile DOS standard si respecta specificatiile ANSI ( American National
Standards
Institute ).
Elemente pregatitoare:
Pentru utilizarea facilitatilor grafice, trebuie procedat astfel:
1. in meniul principal, selectati: Options, Linker, Libraries...;
In caseta de dialog aparuta selectati optiunea Graphics Library.
2. pe la inceputul programului dv. scrieti linia:
#include <graphics.h>
3. in functia main initializati modul grafic: int driver, mod; detectgraph( &driver, &mod ); initgraph( &driver, &mod, "C:\\BC31\\BGI" );
Explicatii:
Driver si mod sunt doua variabile intregi pe care functia detectgraph le initializeaza
cu valori corespunzatoare celui mai adecvat mod video. Compilatorul are intern
o tabela cu driverele grafice pe care le suporta ( fisiere cu extensia BGI ) si cu modurile proprii
fiecarui driver. Dupa apelarea functiei detectgraph, variabila driver va contine numarul de ordine
al celui mai adecvat driver, iar variabila mod numarul de ordine al celui mai adecvat mod grafic
specific driverului respectiv.
De exemplu, daca aveti o placa grafica compatibila VGA, functia detectgraph
va seta valori corespunzand driverului EGAVGA.BGI si modului grafic 640x480 x 16 culori.
Pentru o placa video mai veche de tip EGA, driverul detectat ar fi acelasi, dar modul
grafic ar fi 640x350 x
16 culori.
Variabilele mai sus mentionate sunt apoi trimise ca parametri prin adresa functiei initgraph, ce realizeaza efectiv trecerea placii video in modul corespunzator.
Al treilea parametru este calea spre fisierele driver BGI, in cazul de fata C:\BC31\BGI. Se observa
folosirea secventei
\\, care intr-un program C,C++ produce caracterul '\'.
Exemplu: sa desenam un cerc galben:
#include <conio.h>
#include <stdio.h>
void main()
A int dr, mo; detectgraph( &dr, &mo ); initgraph( &dr, &mo, "C:\\BC31\\BGI";
setcolor( 14 ); // numarul culorii variaza de la 0 la 15, 14 fiind culoarea
galben circle( 320, 240, 100 );
getch(); closegraph();
S
Functia circle( x,y, r ); are ca efect desenarea unui cerc de centru x si y
si raza r. Exemple de alte functii grafice: line( x1,y1, x2,y2 ); // deseneaza o linie intre punctele de coordonate
// (x1,y1) si (x2,y2). Nu modifica pozitia punctului curent
lineto( x,y ); // deseneaza o linie din punctul curent pina in punctul
// ( x,y ), care devine astfel punct curent.
linerel( rx,ry ); // deseneaza o linie din punctul curent pina in
// punctul de coordonate relative ( rx,ry ), care
// devine apoi punct curent.
putpixel( x,y, c ); // deseneaza un pixel in punctul (x,y) de culoare c
moveto( x,y ); // muta pozitia curenta in punctul (x,y). Nu deseneaza nimic. moverel( rx,ry ); // muta pozitia curenta cu rx pozitii la dreapta si ry
// pozitii in sus. rx si ry pot lua si valori negative.
// Valabil si pentru linerel.
Explicatii:
Pozitia curenta este un punct fictiv de pe ecran, situat la inceput in coltul
(0,0). Este folosit si modificat de functii ca lineto, linerel, moveto, moverel, outtext,
etc...
Originea ecranului este in coltul din stinga sus, axa OX este orientata de la
stinga spre dreapta, axa OY de sus in jos.
(0,0) -> (639,0)
_____________________________________________________
| | |
V | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
______________________________________________________
(0,479) (639,479)
Exemplu: grafic1.cpp - Un program care afiseaza un desen simplu. Sa incercam
urmatorul exemplu: grafic2.cpp
#include >conio.h>
#include <math.h>
#include <dos.h>
#include <graphics.h>
void main()
A int dr, mo; detectgraph( &dr, &mo ); initgraph( &dr, &mo, "C:\\BC31\\BGI" );
float u = 0.0;
while( !kbhit() ) // atit timp cit nu se apasa nici o tasta...
A cleardevice(); // sterge ecranul line( 320, 240, 320 + 200*cos(u), 240 + 150*sin(u) ); fillellipse( 320 + 200*cos(u), 240 + 150*sin(u), 50, 50 );
u += 0.01;
S
S
Daca rulati acest program, veti observa un efect neplacut de intermitenta a
colorarii elipsei, si chiar a liniei, efect numit flicker. Cauza este ca intre stergerea
ecranului ( cleardevice ) si redesenarea elipsei, monitorul are timp sa afiseze citeva cadre cu ecranul
negru. Cum se poate avita aceasta? Exista doua metode: folosirea mai multor pagini video si
sincronizarea desenarii cu frecventa de afisare a monitorului, care va face obiectul unui
articol viitor.
Ce inseamna mai multe pagini video? Inseamna ca avem mai multe zone de desenare, fiecare din ele ( pagina ) avind dimensiunea ecranului. In timp ce pe monitor
avem imaginea uneia din ele, noi desenam nestingheriti in cealalta. Cind am terminat de desenat,
inversam intre ele pagina vizibila si pagina de desenare. Putem apoi relua operatiunea, in
timp ce se vede o imagine, noi pregatim imaginea urmatoare.
Interschimbarea paginilor video se face cu ajutorul functiilor grafice setactivepage()
si setvisualpage().
Vom lucra in modul VGA imediat inferior celui returnat de functia detectgraph
pentru o placa VGA, mod cu o rezolutie mai slaba ( 640x350 x 16 ) dar care admite doua
pagini video: pagina 0 si pagina 1. Utilizam o variabila video ce initial are valoarea 0.
O secventa de tipul: setactivepage(video); setvisualpage(1-video); video = 1-video;
Provoaca interschimbarea paginilor si comutarea variabilei video din 0 in
1 si invers. Vom utiliza aceasta secventa in programul grafic3.cpp pentru imbunatatirea efectelor
grafice.
Totusi, chiar daca se observa o oarecare imbunatatire a afisarii, cei cu acceleratoare grafice cu peste 512K, vor observa in continuare fenomenul de clipire. Asta
deoarece placile video mai noi nu mai asteapta semnalul de vertical retrace pentru a schimba
paginile video.
Ce este semnalul vertical retrace? Este momentul in care monitorul a terminat
de afisat un cadru de imagine, moment in care se poate efectua in siguranta schimbarea
paginii de afisare. Pentru acest lucru sunt necesare citeva cunostinte de limbaj de asamblare.
Cam atit deocamdata. In articolul viitor vom vedea cum se poate folosi modul
grafic
320x200 x 256 culori si cam ce se poate face cu el ( rotatii de paleta, deplasari
de imagini,
(pseudo)morphing, si altele ).
Pointeri si tablouri
Sa consideram acum ca avem nevoie sa vedem de ce trebuie sa identificam tipul
unei variabile spre care pointeaza un pointer, ca in:
int *ptr;
Un motiv ar fi ca mai tarziu, cand ptr pointeaza spre ceva, daca
scriem:
*ptr = 2;
compilatorul va sti citi baiti sa copieze in locatia de memorie spre care
pointeaza ptr. Daca ptr a fost definit ca pointind spre un intreg, doi asemenea baiti vor fi copiati,
pentru un long 4 baiti vor fi copiati. Dar definirea tipului spre care pointeaza un pointer permite un numar
de cai interesante in care compilatorul poate interpreta codul. De exemplu, sa consideram un bloc
de memorie constituit din zece intregi in linie. Astfel, 20 de baiti vor fi alocati pentru
10 intregi.
Acum, sa presupunem ca punem ptr sa pointeze spre primul dintre acesti intregi.
Sa presupunem, mai departe, ca acest intreg este locat la adresa 100). Ce
s-ar putea intampla cand am scrie :
ptr + 1; ?
Deoarece compilatorul stie ca este vorba de un pointer (adica valoarea acestuia
este o adresa) si ca acesta pointeaza spre un intreg, aduna 2 la ptr in loc de 1, astfel incit
pointerul sa pointeze spre urmatorul intreg, la locatia de memorie 102. In mod similar, daca
ptr ar fi fost definit ca pointer spre long, compilatorul ar fi adunat 4 in loc de 1. Acelasi lucru se
intampla si cu celelalte tipuri de date, cum ar fi float, double sau structurile definite de utilizator.
In mod similar, ++ptr si ptr++ vor fi echivalent interpretate ca ptr+1, cu sensul discutat in paragraful
anterior. Deoarece un bloc de 10 intregi situat continuu in memorie este, prin definitie,
un tablou, de intregi, se contureaza o interesanta relatie intre tablouri si pointeri. Sa consideram urmatorul
tablou:
int tabai = A1,23,17,4,-5,100S;
Avem un tablou cu sase intregi. Am putea referi fiecare dintre acesti
intregi prin-un index cu valori de la 0 la 5, adica de la taba0i pina la taba5i. Am putea insa sa accesam
aceste elemente prin intermendiul unui pointer ca mai jos:
int *ptr; ptr = &taba0i; /* face ca pointerul nostru sa pointeze spre primul intreg din tablou */
si sa le tiparim folosind notatia tablourilor sau prin dereferentierea pointerului
nostru. Programul urmator ilustreaza acest lucru:
#include <stdio.h> int tabai = A1,23,17,4,-5,100S; int *ptr; int main(void)A int i; ptr = &taba0i; /* point our pointer to the array */ printf("\n\n"); for(i = 0; i < 6; i++)A printf("taba%di = %d ",i,tabaii); printf("ptr + %d = %d\n",i, *(ptr + i));
S return 0;
S
Daca veti compila programul de mai sus, veti observa ca liniile in care se face
afisarea dau rezultate identice. De notat este si modul in care s-a facut dereferentierea
pointerului in a doua linie de afisare : s-a adunat i la acesta si abia dupa s-a dereferentiat noul
pointer. In C, standardul specifica faptul ca oriunde am folosit &nume_variabilaa0i am
putea folosi doar nume_variabila, astfel ca am putea schimba linia
ptr = &taba0i;
cu
ptr = tab;
Acest lucru conduce multe carti la a spune ca numele unui tablou este un pointer.
Desi acest lucru este adevarat, este preferabil sa va ginditi la numele de tablouri
ca la pointeri constanti. De exemplu, putem scrie ptr = tab; dar nu putem scrie tab = ptr;.
Motivul este ca ptr este o variabila, pe cind tab este o constanta, locatia
la care va fi stocat primul element al tabloului nu mai poate fi schimbat odata ce tabai a
fost declarat.
Deocamdata atit la rubrica "C/C++", in numarul urmator vom continua
explicatiile asupra acestui capitol "spinos" si vom analiza, probabil, structurile mai
in detaliu.