Cum sa modific valoarea unei constante in C?

Aceasta scurta istorie e despre unicul lucru constant din univers - schimbarea... Ce ar fi daca pi era diferit de 3.14...? Si daca e ar fi fost diferit de 2.71...? Si daca phi era altceva decit 1.618...? Astazi nu voi discuta despre aceste lucruri, ci voi discuta despre const in C ;-)

When modifying a data declaration, the const keyword specifies that the object or variable is not modifiable. When following a member function's parameter list, the const keyword specifies that the function doesn't modify the object for which it is invoked.

Da? Sau poate totusi e posibil?

const int c=5;
int *p, b;

p=(int*)&c;
*p=3;

b=*p; //b=3
b=c; //b=5
b=*(int*)&c; //b=3

Ce se petrece aici?MSVC71 p este un pointer care ia adresa constantei c, si apoi se modifica continutul acelei adrese de memorie spre care point'uieste p. Cu alte cuvinte, daca ne uitam cu debuggerul, vedem urmatoarele:

Mda... valoarea lui b este 5, si nu 3. E natural sa ne intrebam ce se petrece, si cum se explica aceasta.

Sa incercam in alta parte, cu un alt compilator, pe un alt OS. constant-changeAici se petrece acelasi lucru - continutul memoriei se modifica, dar cind folosim undeva constanta, valoarea este oricum acea 'constanta', si nu acea pe care am modificat-o artificial.

Pina la urma s-a ajuns la concluzia ca ceva nu e in regula... Sau noi ne-am timpit, sau lumea intreaga e nebuna ;-) Sapind un pic mai adinc, s-a ajuns la concluzia ca acea memorie in care se stocheaza valoarea constantei nu este utilizata. De fapt, daca codul este dezasamblat, totul devine evident - peste tot valoarea constantei este 'hard coded', adica in nici unul din cazuri nu se citeste nici-o adresa de memorie, ci se transmite direct o valoare; de parca undeva in cod s-a scris #define c 5

De fapt, asa am si facut:

#define HZ_ 5
int _tmain(int argc, _TCHAR* argv[])
{
const int c=5;
int *p, b;

p=(int*)&c;
printf("%i ", c);
*p=3;

b=*p;
b=c;
b=HZ_;

Si iata cum arata aceasta la nivel inferior:

MSVC71dasm

Deci, care sunt concluziile?

Si daca tot suntem aici si discutam despre C:

int z[3]={1,2,3};

b=z[0];
b=1[z];

Ce se intimpla in linia #3? Se intimpla b=z[1]. De ce? Pentru ca un array este de fapt un pointer, iar un pointer este de fapt un array.

b=z[0] este echivalentul b=*(z+0), iar b=z[1] este echivalentul b=*(z+1), iar b=*(1+z) este acelasi lucru ;-)

Nota: pointer arithmetic este o chestie care difera de asteptarile unui om non-initiat in C. Spre exemplu, z+3 nu te duce la z + offset de 3 bytes, ci la z + 3*sizeof(z) bytes (z e int, deci se face 3*sizeof(int), adica ne deplasam nu cu 3 bytes, ci cu 3 valori intregi mai departe). De fapt, daca citesti aceasta pagina, atunci nota data ar trebui sa-ti fie cunoscuta. ;-)

Acum ramine sa speram ca universul este un sistem extrem de fiabil, in care constantele sunt 100% constante :-)

Comments

virusok — 2007-03-18 15:28

Post:

int z[3]={1,2,3};

b=z[0];
b=1[z];

ai aici in ultimul rind o eroare :)
I hope you can see it
In rest articolul e destul de interesant.
Si pentru e destul de informativ :)

Thx for what u doing. See ya!

gr8dude — 2007-03-18 17:48

Virusok, unica eroare pe care o vad eu acolo e ca nu se declara nicaieri b, dar se presupune ca b a fost declarat undeva mai sus, chiar in primul fragment de cod.

Daca spui ca eroarea este in 1[z], atunci nu ai citit atent comentariile ;-) Acolo se explica ca z+1 este 1+z (comutativitate, like in the good old days)

Constantin — 2007-03-18 18:05

Desi se comporta la fel in VC si in GCC, ceea ce descrii in articol e nedefinit conform standardului (http://www.possibility.com/Cpp/const.html), fiindca nu se stie unde compilatorul stocheaza constantele. De obicei, cind declari o singura constanta, compilatorul isi asuma dreptul sa faca “find and replace” in cod, asa cum ar face preprocesorul cu #define-urile. Astfel, valoarea constantei e cunoscuta la momentul compilarii. Din cauza asta urmatorul cod functioneaza:

const int maxn = 100;
int v[maxn]; //OK

Iar acesta nu:

const int magic[3] = { 100, 200, 300 };
int smth[ magic[2] ]; //whoops!

(fiindca la compile-time, nu se stie cit e magic[2])

Mai este o metoda, cu enumerari anonime, care e preferata mea:

enum { magic1 = 100, magic2 = 200, magic3 = 300 };
int another[magic3]; //OK

P.S. De ce nu pot folosi tagul <a> in comentarii?

Constantin — 2007-03-18 18:24

Inca ceva:

const int magic[2] = { 100, 200 };
cout << magic[0] << ‘ ‘ << magic[1] << endl;

int *p = (int *)&magic[0];
*p = 123;

cout << magic[0] << ‘ ‘ << magic[1] << endl;

Asta da segmentation fault:

Program received signal SIGSEGV, Segmentation fault.
0x08048749 in main () at const-mine2.cpp:10
10 *p = 123;
(gdb) p p
$1 = (int *) 0x804887c
(gdb) p &magic[0]
$2 = (int *) 0x804887c

Se vede ca memoria respectiva e read-only.