A memória felosztása
Kis áttekintés¶
Ahhoz, hogy jobban megértsük, az egyes adatokat fizikailag hogyan is tárolja a programunk, és miért és hogyan tudunk adott esetben optimálisabb kódot írni C-ben, ha az adatokkal ügyesen bánunk, meg kell ismerjük azt, hogy egy program hogyan foglal helyet a memóriában.
Memória felépítése¶
Az alábbi kép mutatja, hogyan épül fel általánosságban egy C program memóriája. x86-on a verem (stack) a magasabb memóriacímek felől nő az alacsonyabb címek felé (ahogy az ábra is mutatja), ám ez nem szabvány, más architektúrákon fordítva lehet.
A memóriaterületekről néhány szó:
- A stacken (veremben) tárolódik egy-egy függvényhívás adata, vagyis (egyebek mellett) a lokális változók, a függvényeknek átadott paraméterek. A felszabadítás automatikusan, a meghívott függvényből való visszatéréskor megtörténik (emiatt veszélyes egy függvény lokális változójának memóriacímét visszaadni, hiszen az a terület automatikusan felszabadul).
- A heap-en/dinamikus memóriában tárolódik mindaz, amit dinamikusan (
new
,malloc
,calloc
, ...) foglaltunk le. A heap általában az alacsonyabb címek felől növekszik a magasabb címek felé. Ennek felszabadítása nem automatikus, a programozónak kell nem elfelejtenie (delete
,free
). Ezt a memóriaterületet használják a dinamikusan betöltött library-k is. Elérése költségesebb, mint verem esetén, de a mérete nagyobb, illetve átméretezhető. - BSS (Block Started by Symbol, Uninitalized Data Segment): olyan globális és statikus változók kerülnek ide, amelyeket a programozó nem inicializált saját maga, ezért a program indulásakor automatikusan 0 értéket kapnak. Például a globális változók vagy a függvények
static
kulcsszóval ellátott változói. - DS (Initalized Data Segment): olyan globális és statikus változók, amiket a programozó ellátott kezdőértékkel.
- Text: a programból készült gépi utasítások helye.
- Environment: parancssori argumentumok, környezeti változók helye.
A memóriának ezeket a területeit szegmenseknek nevezzük. Minden szegmens meghatározott szabályokkal rendelkezik. Például, hogy az adott szegmens adatai írhatóak-e, vagy sem! Ha az adott szegmens szabályait megsértjük, kapjuk az úgynevezett szegmentálási hibát, vagy segfaultot, ami a programok talán leggyakoribb abortálását okozza.
Tip
A size
programot ha alkalmazzuk a binárisra, amit futtatunk, megtudhatjuk, hogy az egyes szegmensekre mekkora terület jut.
Változók és memória¶
A számítógép fenti memória modelljét ezután úgy képzeljük el, mintha rekeszekre bontanánk, ahol minden egyes rekesz 1 byte adatot képes tárolni. (Tulajdonképpen a C-ben mind a kód részre, mind az adat részre tudunk így tekinteni.) Minden rekeszhez egy egyedi sorszám tartozik, amit memóriacímnek hívunk. Ez a cím határozza meg, hogy egy adott adat pontosan hol található a memóriában.
Amikor egy adott típusú változót használunk a programban, akkor valójában azt mondjuk meg, hogy a memóriának egy hány bájtból álló darabját szeretnénk használni. (A változó egyéb jellemzői, illetve létrehozásának módja megadja, hogy mely memória szegmensben kerül tárolásra.) Például egy int
típusú változó általában 4 byte-on tárolódik, tehát négy egymás melletti memóriacímre lesz szükség.
A változók memóriában való tárolása tehát két fontos információn alapszik:
- a kezdőcím (az első byte címe, ahol az adat tárolódik),
- és az adattípus mérete (hány byte-on terül el az adat).
Ez alapján a fordítóprogram pontosan tudja, hogy hogyan értelmezze az adott memóriaterület tartalmát – vagyis hogyan olvassa ki vagy módosítsa az ott tárolt értéket.
Tegyük fel, hogy ezt írjuk a programba:
1 |
|
Ez mit jelent a memóriában?
- A
x
nevű változónk int típusú, tehát (a legtöbb rendszeren) 4 byte-ot foglal. - A fordító lefoglal neki egy szabad memóriaterületet (például a
0x1000
címről kezdődően). - A változó értékét, azaz a 5-öt bináris formában eltárolja ezen a 4 byte-os memóriaterületen.
Memóriakép:¶
1 2 3 4 5 6 |
|
(Tegyük fel, hogy kis endian a rendszer, vagyis a legkisebb helyiérték kerül a legkisebb címre.)
Magyarázat:¶
- A
5
decimális szám binárisan:0000 0000 0000 0000 0000 0000 0000 0101
- Ez hexadecimálisan:
0x00000005
- Ez a 4 byte a
0x1000
címtől kezdve fordított sorrendben kerül tárolásra a kis endian miatt: 0x1000
: 0x05 (legalacsonyabb byte)0x1001
: 0x000x1002
: 0x000x1003
: 0x00