offline
- Fil
- Legendarni građanin
- Pridružio: 11 Jun 2009
- Poruke: 16586
|
- 14Ovo se svidja korisnicima: NIx Car, vasa.93, morando, Srki_82, Aco, _Sale, KlinkaPalacinka, mcrule, ivance95, E.L.I.T.E., DzoniB, Srki94, vladasimovic, Filodendron
Registruj se da bi pohvalio/la poruku!
Za 7000. poruku --> novi članak
Ovaj članak treba da pruži detaljan uvid o tome koliko rad sa pokazivačima može biti "muka Tantalova", ukoliko se pravilno ne rukuje sa njima.
Predstavljen je jednostavan i vrlo kratak kod, a pažnja će se usmeriti na stvari koje se dešavaju "ispod haube", dakle kuda "divljaju" pointeri. Ući u trag ovom bug-u, u glomaznom projektu, prava je muka.
Za potrebe članka je korišteno:
- Razvojno okruženje ZinjaI --> više informacija
- Linux Mint 13 KDE, x86 --> više informacija
Pogled na kod i izlaz iz programa:
Kompletan kod:
#include<iostream>
using namespace std;
typedef unsigned short int USHORT;
int main (int argc, char *argv[])
{
USHORT *pInt =new USHORT;
*pInt =10;
cout << "*pInt:" << *pInt << endl;
delete pInt;
long *pLong =new long;
*pLong =90000;
cout <<"*pLong:" << *pLong << endl;
*pInt =20;
cout <<"*pInt:" <<*pInt <<endl;
cout <<"*pLong:" <<*pLong <<endl;
delete pLong;
return 0;
}
Napomena: u zavisnosti od mašine, izlaz iz koda može izgledati drugačije.
Dakle, pitanje glasi kako to da pLong ima vrednost 65556, kada njega nismo menjali !?
Krenimo sa analizom:
- U liniji 9 se deklariše pointer pInt, rezerviše se slobodna memorijska lokacija na heap-u i pointer pokazuje na taj prostor. Tu lokaciju sam označio sa proizvoljnom oznakom: "#".
- U liniji 10 se broj 10 upisuje u rezervisanu memorijsku lokaciju #.
Bitno je napomenuti da se u memoriji ne čuvaju decimalne vrednosti, već heksadecimalne vrednosti. Heksadecimalne vrednosti koje odgovaraju vrednostima iz koda su:
10 --> A (tj. 00 0A za vrednost od dva bajta)
90000 --> 00 01 5F 90
20 --> 14 (tj. 00 14 za vrednost od dva bajta)
Pri određivanju ovih vrednosti može vam pomoći sledeći Web servis:
EasyCalculation - Hex To Decimal and Binary Converter --> LINK
// takođe, pogledajte ovu kompilaciju korisnih Web servisa, među kojima je i ovaj gore pomenuti: LINK
- U liniji 14 oslobađa se memorijski prostor na koji pokazuje pointer, ali pointer i dalje pokazuje na tu memorijsku lokaciju.
Dakle, u primeru je namerno izvršen prevod i pointeru nije dodeljen NULL, tj. stvoren je divlji pointer (wild, dangling ili stray pointer).
- U liniji 16 se deklariše pointer pLong, rezerviše se slobodna memorijska lokacija na heap-u i pointer pokazuje na taj prostor.
Kako je lokacija # postala slobodna za upotrebu, kompajler je prvu slobodnu lokaciju (dakle, #) odredio kao lokaciju na koju pokazuje pLong.
- U liniji 17 se u tu memorijsku lokaciju upisuje broj 90000. Ilustrativan prikaz memorije nakon ove linije:
Sada dolazimo do još jednog nivoa usložnjavanja. Uočimo da je:
90000 --> 00 01 5F 90 u heksadecimalnom zapisu, a na slici iznad je prikazano: 5F 90 00 01.
// dakle, parovi: 1-2-3-4, su sada: 3-4-1-2
Za ovaj način skladištenja je "kriv", tzv. byte swapping order. Zbog toga stoji uvodna napomena u tekstu. U zavisnosti od toga kako kompajler dodeljuje slobodnu memoriju i byte swapping order-a, moguće je da se dobiju različiti rezultati, u odnosu na postavljeni screenshot.
Više o terminima byte swapping, big endian i little endian i skladištenju možete naći na sledećim linkovima:
Byte swapping and binary files - http://paulbourke.net/dataformats/endian/
Big endian & Little endian - http://en.wikipedia.org/wiki/Endianness
Pimer funkcije u Matlab-u - http://www.mathworks.com/help/matlab/ref/swapbytes.html
- U liniji 20 se u memorijsku lokaciju na koju pokazuje pointer pInt upisuje vrednost 20. Kako pInt i dalje pokazuje na memorijsku lokaciju #, vrednost će se upisati na tu memorijsku lokaciju. Sada je izgled memorije ovakav:
Šta se zapravo ovde dešava?
--> Kako je short int veličine 2 bajta, a long veličine 4 bajta dolazi do situacije "stomp on a pointer". Dakle, vrednost 20 (veličine 2 bajta) je pregazila prva dva bajta postojeće vrednosti 90 (koja je veličine 4 bajta).
Dakle, kada se na 5F 90 00 01 nalepi 00 14 --> 00 14 00 01
Pošto je decimalna vrednost, koja odgovara heksadecimalnoj vrednosti za 00140001, iznosi 1310721, neko će se zapitati zašto ova vrednost nije izlistana kao konačna vrednost u izlazu iz programa (vidite početni screenshot, izlaz za pLong je: 65556).
Ko je krenuo gornjim rezonom - pogrešio je, jer se povučena vrednost iz memorije transponuje po "byte swapping order-u"
Kako smo rekli u tekstu, ovo su parovi: 1-2-3-4 --> 3-4-1-2.
Nama, pri povlačenju vrednosti iz memorije treba obrnut proces: 3-4-1-2 --> 1-2-3-4
Dakle: 00 14 00 01 [ili 3-4-1-2] --> 00 01 00 14 [ili 1-2-3-4]
Heksadecimalnoj vrednosti od 00010014 odgovara decimalna vrednost: 65556, i najzad to je vrednost koju vidimo kao izlaz
Naravoučenije
Prilikom rada sa pokazivačima treba biti vrlo oprezan, dakle:
- osloboditi memorijski prostor (rezervisan sa NEW) preko ključne reči DELETE (ako se ovo ne uradi, doći će do memory leak-a).
- dodeliti pointeru NULL (ili nullptr) , ako smo sa delete oslobodili prostor sa odgovarajuće memorijske lokacije.
Ukoliko ovo ne uvažimo doći će do različitih nepredviđenih situacija, koje se teško mogu uloviti.
|