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:

  1. int const i = 10;

    i este o constanta care are valoarea 10. O expresie de atribuire de forma i = 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 forma y = i + 7 este corecta si va atribui lui y valoarea 17.
  2. double const pi = 3.14159265;

    declara numele pi ca fiind o constanta de tip double si care are valoarea 3.14159265.
    O atribuire de forma pi = 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)
  3. char *const s = "sir";

    Aici tip este char *, 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 forma s = t;, unde t este un pointer spre caractere, nu este acceptata de compilator, atribuirile:

    *s = '1';
    *(s+1) = '2';
    sunt corecte. Cu ajutorul lor se schimba caracterul s cu 1 si i cu 2 in zona spre care pointeaza s.

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.

8.6. Functii standard utilizate la prelucrarea sirurilor de caractere