8.5. Modificatorul const
2019/03/10 in Programare in C
Am vazut la Capitolul 1 ca o constanta se poate defini prin caracterele care o compun.
Intr-adevar, caracterele din compunerea unei constante definesc atat tipul cat si valoarea acesteia.
Exemple:
1234 | constanta de tip int |
1.5 | constanta de tip double |
'a' | constanta caracter |
"a" | constanta sir |
Tot in Capitoul 1 s-au definit constantele simbolice. O astfel de constanta se defineste cu ajutorul constructiei #define tratata de preprocesor. Ea are urmatorul format:
#define nume succesiune_de_caractere
Printr-o astfel de constructie se atribuie un nume unei constante.
La preprocesare, nume se inlocuieste peste tot cu succesiune_de_caractere, exceptand cazurile cand nume se afla intr-un comentariu sau sir de caractere.
La compilare, nume definit printr-o constructie #define nici nu exista in afara comentariilor si a sirurilor de caractere. De aceea, valoarea ei nici nu poate fi modificata la executia programului.
In limbajele C si C++ exista posibilitatea de a defini date constante folosind modificatorul const in declaratii. Printr-o astfel de declaratie, unui nume i se poate atribui o valoare initiala care nu poate fi modificata, in mod obisnuit, printr-o expresie de atribuire, ca in cazul variabilelor. De aceea, o data declarata cu ajutorul modificatorului const se considera ca este o constanta.
Formatele posibile ale unei declaratii cu modificatorul const sunt:
tip const nume = valoare;
tip const *nume = valoare;
const tip nume = valoare;
const tip *nume;
const nume = valoare;
O declaratie de forma celor de mai sus se spune ca este o declaratie de constanta.
O declaratie de constanta de forma (1) tip const nume = valoare;
este asemanatoare cu o declaratie de variabila obisnuita de forma
(2)tip nume = valoare;
Diferenta dintre cele doua declaratii consta consta in aceea ca valoarea lui nume atribuita prin declaratia (1) nu poate fi schimbata
folosind o expresie de atribuire de forma nume = expresie
,
in timp ce aceast lucru este posibil pentru nume declarat prin declaratia (2).
Exemple:
int const i = 10;
i este o constanta care are valoarea 10. O expresie de atribuire de formai = 3
este eronata. Constanta i declarata ca mai sus difera fata de o constanta definita prin #define, deoarece ea nu este prelucrata de preprocesor. Numele ei exista la compilare.
O expresie de formay = i + 7
este corecta si va atribui lui y valoarea 17.double const pi = 3.14159265;
declara numele pi ca fiind o constanta de tip double si care are valoarea 3.14159265.
O atribuire de formapi = 3.1415
genereaza o eroare la compilare. pi poate fi utilizat in expresii de forma:
pi*r*r sin(pi/2 + x) cos(x - pi)
char *const s = "sir";
Aici tip estechar *
, deci s este pointer spre zona in care se pastreaza sirul de caractere format din literele s, i, r si caracterul NUL. In acest caz, valoarea lui s nu poate fi schimbata. El fiind pointer, are ca valoare o adresa a unei zone de memorie de dimensiunea egala cu 4 octeti. In aceasta zona se pastreaza sirul de caractere indicat mai sus. Continutul acestei zone poate fi modificat. Astfel, in vreme ce o atribuire de formas = t;
, unde t este un pointer spre caractere, nu este acceptata de compilator, atribuirile:
sunt corecte. Cu ajutorul lor se schimba caracterul s cu 1 si i cu 2 in zona spre care pointeaza s.*s = '1'; *(s+1) = '2';
Declaratia (3) tip const *nume = valoare;
defineste pe nume ca un pointer spre o zona constanta.
In acest caz, valoarea pointerului nume se poate schimba. De exemplu, daca se considera declaratia:
char const *s = "sir";
atunci atribuirea:
s = t;
unde t este un pointer spre char este corecta. In schimb, atribuirile:
*s = 'a';
*(s+1) = 'b';
sunt eronate, deoarece s pointeaza spre o zona in care se pastreaza o data constanta. Cu toate acestea, constanta respectiva poate fi modificata folosind un pointer diferit de s:
char *p;
p = (char *)s;
/* p, ca si s, pointeaza spre zona in care se pastreaza sirul de caractere "sir" */
*p = 'a';
*(p+1) = 'b';
Aceste atribuiri sunt corecte, deoarece p nu mai este un pointer spre o zona constanta.
Declaratia const tip nume = valoare;
este identica cu declaratia (1) daca tip nu este un tip pointer.
Daca tip este un tip pointer, adica declaratia (4) este de forma (5) const tip *nume = valoare;
, atunci ea este identica
cu declaratia (3) de mai sus. De obicei, se utilizeaza formatul (5), in locul formatului (3) pentru a declara un pointer spre o zona constanta,
a carei valoare nu poate fi modificata direct, adica prin atribuiri in care se foloseste nume:
*nume = ...
*(nume+k) = ...
Declaratia (6) const tip *nume
se utilizeaza pentru a declara un parametru formal.
Fie functia f de antet:
tip f(tip *nume)
La apelul functiei f, parametrului formal nume i se atribuie ca valoare o adresa. Am vazut ca acest fapt da posibilitatea functiei f sa modifice data pastrata in zona de memorie spre care pointeaza nume folosind o atribuire de forma:
*nume = valoare
Exista cazuri in care functia apelata in acest fel nu are voie sa modifice data din zona de memorie a carei adresa este atribuita unui parametru formal. Ea trebuie numai sa aiba acces la data respectiva. Pentru a proteja data de atribuiri neautorizate, vom declara parametrul formal respectiv ca un pointer spre o data constanta. In acest caz, antetul functiei f devine:
tip f(const tip *nume)
In acest caz, o atribuire de forma:
*nume = valoare
este interzisa. De aceea, declaratia:
const tip *nume
se recomanda a fi utilizata pentru oricare parametru formal care la apel are ca valoare adresa unei zone de memorie al carei continut nu poate fi modificat de functia apelata.