Yapılarda, adreslemede beklenmedik sıkıntılar

Daha önce birkaç kere başma gelen, suçu compiler a bulduğumuz veya programa cin musallat olmasından şüphelendiğimiz bir durumun aslında tamamen low-level addressing ile ilgili olduğunu öğrendim.

Detaylıca bahsedeyim. bir struct array indeki elemanların sayısını, array in toplam boyutunu (byte cinsinden), bir elemanın boyutuna bölerek hesaplayan bir kod parçası, struct içindeki elemanların yerlerini değiştirince farklı sonuçlar veriyordu. ilk bakışta saçma gibi görünse de, struct içindeki elemanların sırasının değişmesi, bu struct’ların boyutlarını değiştiriyor, dahası, bu struct ların array içinde yerleştirilmesi sırasında aralarda padding kullanılmasından doğan farklılıklar oluşuyor.

Adreslerin hesaplanmasındaki farkın temel sebebi “Address alignment”. Yani her entity, kendi boyutuna göre bir adres yapısına sahip olmak zorunda. 8byte lık bir entity nin adresinin son 3bit i 0 olmalı, yani adresi 8in katları biçiminde olmalı. şimdi bu struct ları inceleyelim: 32bit bir makinede, char=1, short=2, int=4, long=8 byte.


struct st1 {    
    char c;       0x000000  (0 - 1)
    long ln;      0x001000  (8 - 15)
    int i1;       0x010000  (16 - 19)
};                    

 

struct st2 {
    long ln;     0x000000 (0 - 7)
    int i1;      0x001000 (8 - 11)
    char c;      0x001100 (12 - 13)
};

 

struct st3 {
    long ln;     0x000000 (0 - 7)
    int i1;      0x001000 (8 - 11)
    int i2;      0x001100 (12 - 15)
};

3 struct ın da 0x0000 adresinde başladığını varsayarsak, yukarıdaki kurala göre art arda elemanları sıraladığımızda, her elemanın başladığı adres ve hangi bytelar arasını kapladığı (inclusive) yanında görülüyor. İlk iki struct içinde aynı elemanlar farklı sıralarda yazıldığında aynı struct ın boyutu 20byte’dan 14byte’a iniyor. Bug ın sebebini öğrenmekle kalmadık, ayrıca %30 yer kazandık!

st1 ve st3 e baktığımızda, elemanların boyutlarını toplayarak struct boyutunu bulmaya çalışırsak sizeof(st1) = 1 + 8 + 4 = 13 sizeof(st3) = 8 + 4 + 4 = 16 bekliyoruz. st3 içinde hiç bir padding bit kullanmamıza gerek kalmadığı için sonuç doğru geliyor. Ama st1 içinde kullanılan padding byte lardan dolayı, st3 ün de üzerine çıkıp, 20byte lık bir boyuta ulaşıyor. gerçek sizeof operatörü tabi ki bu ayrıntılardan haberdar ve bize sizeof(st1) sonucunda doğru değeri dönecek. Ancak diğer ayrıntı array içinde bu struct ların yerleştirilmesinde gizli.

Bir struct ın adresi, içindeki en büyük elemanın boyutunun katı şeklinde olmalı. Yani içinde long, int, char bulunan bir struct, 0x00000010 adresinden başlanarak memory ye yerleştirilemez. Sonraki ilk 8in katı olan adrese kadar boşluk bırakılıp 0x00001000 adresine yerleştirilir.

Bu durumda st1 arr1[6] ve st2 arr2[6] array lerinin adreslenmelerini inceleyelim:

arr1[0]……0x00000000 (0-19)

arr1[1]……0x00011000 (24-43)

arr1[2]……0x00110000 (48-67)

arr1[3]……0x01001000 (72-91)

arr1[4]……0x01100000 (96-115)

arr1[5]……0x01100000 (120-139)

boyut: 140byte arr2[0]……0x00000000 (0-13)

arr2[1]……0x00010000 (16-29)

arr2[2]……0x00100000 (32-45)

arr2[3]……0x00110000 (48-61)

arr2[4]……0x01000000 (64-77)

arr2[5]……0x01000000 (80-93)

boyut: 94byte

140-94/140 ~= %33 yer kazandık. eleman sayısını bulmak için diziyi struct boyutuna bölecek olsak, 140/20=7 94/14=6 aynı elemanlara sahip, aynı sayıda farklı iki structın kullanıldığı iki array için farklı sonuçlar almış olacağız.

Tabiki array içinde kaç eleman olduğunu bulmak dışında sebebini anlamdığımz başka “bug” larda da bu hesaplamaları ve alignment (adresleme) mantığını bilmek faydalı olacaktır.

Salih AHİ

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s