Tür Dönüştürme Operatörleri

C programlama dilinde tür dönüştürme sadece bir tane “cast”(tür dönüştürme) operatörüyle  kolaylıkla yapılabiliyordu. Örneğin;


int a = 5;

short b = (short) a; // operator() burda tür dönüştürme operatörü olarak kullanılıyor

C++ da ise durum biraz daha farklı. Tür dönüştürme işlemi biraz daha detaylı olarak ele alınmış. Şöyleki tür dönüştürme işlemini neyi amaçlıyorsanız o amaca uygun tür dönüştürme operatörünü kullanmalısınız. Şimdi tek tek bu operatörleri inceleyelim.

dynamic_cast (dinamik tür dönüştürme operatörü):

Referanslar ve göstericiler için kullanılır. Geri dönüş değeri istenilen türe dönüştürme işlemi başarısız ise NULL’dır. Genel kullanım alanlarından biri çalışma zamanında tür belirlenmesi (Run Time Type Identification) içindir. Örnek kod üzerinden ilerlersek:


#include <iostream>

using namespace std;

class Hayvan
{

public:

    virtual void turIsmi(){cout << "Tür ismi: Hayvan" << endl;};
};

class Aslan: public Hayvan
{
public:
    virtual void turIsmi() {cout << "Tür ismi: Aslan" << endl;};

};

int main(int argc, char** argv)
{
    Hayvan *h1 = new Hayvan();
    Hayvan *h2 = new Aslan(); //burda aslında gizli bir tür dönüştürme işlemi var. Yine burda da dynamic_cast operatörü kullanılabilir.
    Hayvan *h3;

    if ((h3 = dynamic_cast<Aslan*>(h1)) == NULL)
        cout << "h1 Aslan türüne dönüştürülemez." << endl;

    if ((h3 = dynamic_cast<Aslan*>(h2)) == NULL)
        cout << "h2 Aslan türüne dönüştürülemez" << endl;

    h3->turIsmi();

    delete(h1);
    delete(h2);

  return 0;
}

Görüldüğü üzere Hayvan ve Aslan türünden birer nesne dinamik olarak oluşturuluyor. Daha sonra bu nesneler
Hayvan* türünden bir göstericiye atanıyor.Eğer neden Aslan* türünden bir nesne nasıl oluyorda Hayvan* türüne atanıyor diye sorarsanız dilin tasarımından dolayı diye cevaplandırırım. Kaldı ki bu tasarım daha öncede dediğim gibi çalışma zamanında tür belirlememize olanak sağlıyor. Bunun en geniş örneklerini GUI programlamada görebilirsiniz. Neyse konumuza geri dönelim.

Daha sonra ise dynamic_cast operatörüyle bu göstericiler Hayvan* türüne dönüştürülmeye çalışılmış tabi ki bunlardan h1 göstericisi Hayvan türüne ait bir gösterici olduğundan dynamic_cast operatörü NULL dönecektir.

h2 göstericisi(pointer) ise Aslan türünü gösterdiği için tür ödnüştürme işlemi başarılı bir şekilde gerçekleşecek. Ve 32.satırda çağırılan fonksiyon Aslan sınıfına ait olan sanal fonksiyondur.Böylelikle ekrana

Tür ismi: Aslan

yazılacaktır. Eğer burda göstericiler yerine referanslarla işlem yapsaydık başarısızlık durumunda dynamic_cast operatörü bad_cast türünden bir kural dışı nesnesi (exception) fırlatacaktır.

static_cast operatörü:
static_cast te dynamic_cast te olduğu gibi dinamiklik söz konusu değildir. Yani çalışma zamanında türün dönüşüp dönüşemediğini belirleyemezsiniz. Dolayısıyla static_cast’te tüm sorumluluk programcıya aittir. Örneğin:

class Hayvan
{

public:

void turIsmi(){cout << "Tür ismi: Hayvan" << endl;};
};

class Aslan: public Hayvan
{
public:
void turIsmi() {cout << "Tür ismi: Aslan" << endl;};

};

int main(int argc, char** argv)
{
Hayvan *h1 = new Hayvan();
Hayvan *h2 = new Aslan();

Aslan *h3;

h3 = static_cast<Aslan*>(h1);

h3->turIsmi();
delete(h1);
delete(h2);

return 0;
}

static_cast operatörüyle Hayvan* türünden bir nesne Aslan*’a dönüştürülerek yine Aslan* türünden bir göstericiye atanmıştır.
Çalışma zamanında bu kodun ne yapacağı belirsizdir(ambiguity). Bunun haricinde bu operatörle derleyici tarafından gizli(implicit)
bir şekilde yapılan tüm tür dönüştürmelerini de yapabilirsiniz.

reinterpret_cast:

Her türlü gösterici türünü  her türlü  gösterici türüne çevirebilir. Bilinçsiz kullanımı kesinlikle tavsiye edilmez. Aslında basit olarak
göstericinin diğer göstericiye binary olarak kopyalanmasıdır. Hash fonksiyonlarında pratiklik açısından adres olarak kullanılan alanı hash değerine çevirmek için kullanılır. Kullanım alanları oldukça kısıtlıdır. Örneğin;


#include <iostream>

using namespace std;

int main()
{
int a = 5;

unsigned int *b = reinterpret_cast<unsigned int*>(&a + 1);

cout << *b << endl;

return 0;
}

const_cast:

Bir türün değişmezliğini manipüle etmemizi sağlar. Örneğin:


#include <iostream>

using namespace std;

void printf(char *p)
{
cout << "char * " << p << endl;
}

int main(int argc, char** argv)
{
const char *s = "islam yasar";

char *p = const_cast<char*>(s);

printf(p);

return 0;
}

Görüldüğü üzere const olan bir nesnenin normalde const olmayan bir nesneye atanması mümkün değil. Bu tür dönüştürme operatörü ile mümkün hale geliyor.

NOT:

Madem çalışma zamanında tür belirlemeden bahsettik typeid operatörünü de araya sıkıştıralım.

typeid:

bu operatör basitçe çalışma zamanında gelen nesnenin türünü belirlememizi sağlıyor. Daha önce bunu dynamic_cast operatörüyle yapmıştık.


Hayvan *h1 = new Hayvan();
Aslan *h2 = new Aslan();

if (typeid(h1) == typeid(Aslan*))
cout << "h1 Aslan* türünden" << endl;

if (typeid(h2) == typeid(Aslan*))
cout << "h2 Aslan* türünden" << endl;

İslam Yaşar

Advertisements

Private key kullanarak https çözümleme – 2

viewssld’nin nasıl çalıştığını daha iyi anlamak için önce ssl protokolünde verilerin nasıl şifrelendiğini anlamamız lazım.

Bir ssl bağlantısı temelde handshake ve application data flow diye tabir edeceğimiz iki ana kısımdan oluşur. Uygulama ile ilgili bilgilerin geçtiği application data flow kısmına geçmeden önce, tarafların bu verileri nasıl şifreleyecekleri konusunda bir anlaşmaya varmaları gerekir. Birbiri ile ilk kez iletişime geçen bu iki tarafın veri aktarımına geçmeden önce bağlantı hakkında emin olmaları gereken 3 konu bulunur.

Authenticity: Karşı tarafın gerçekten istenilen kişi olması. Genelde yalnızca server authentication yeterli olsa da, bazı durumlarda client’ın da kimliğini doğrulaması gerekebilir. Örneğin elinize bir mektup geçtiğinde bu mektubun gerçekten “Gönderen” kısmında belirtilen kişiden geldiğine emin olmanız gerekir.
Security: Aktarılan paketlerin yalnızca alıcı tarafından okunabilmesi. Paketleri sniff eden üçüncü bir kişi için bu veriler tamamen anlamsız olmalı. Elinizdeki zarfın başkası tarafından okunmamış olmasını istersiniz

Integrity: Verilerin karşı tarafa gönderildiği şekilde ulaşabilmesi. Araya giren herhangi birinin paketleri değiştirerek yollaması durumunda bu fark edilebilmeli. Bir zarfın açılmamış olması, bu zarfı son kapatan kişinin gönderen olduğunu garanti edemez. Başka birisi mektubu okuyup, hatta değiştirip yeniden kapatmış olabilir.

Handshake sırasında bu üç konunun nasıl çözüleceği cipher suite’ler kullanılarak belirlenir. Örneğin TLS_RSA_WITH_RC4_128_SHA kullanılıyorsa, kimlik doğrulama için RSA, şifreleme için RC4, veri doğrulama için SHA algoritmaları kullanılacak demektir. Bizim bu yöntemde ihtiyacımız olan, RSA dışında bir doğrulama yöntemi kullanılmamış olması. Şifreleme için kullanılan algoritma zaten OpenSSL tarafından çözüleceği için hangi algoritmanın kulllanılacağını tespit etmemiz yeterli olacak. Pasif saldırı yaptığımız için zaten integrity konusunda yapmamız gereken bir şey yok.

Önce Handshake sırasında gelişen olaylara bakalım:

Handshake ile ilgili detaylı bilgilere girmeyeceğim. Bizim için önemli olan konu, 11’de aktarılan encrypted mesajların nasıl decrypt edileceği. Bunun için bulmamız gereken temel bilgi ise 7’de oluşturulan Master Secret. Peki nedir bu Master Secret?

Master Secret; application data flow sırasında kullanılacak çeşitli key’lerin hesaplanması için kullanılan esas key olarak düşünülebilir. Pseudo Random Function denilen özel random fonksiyonları, belirli seed kelimelerle çalıştırılarak bu master secret‘dan farklı şifreler üretilir. Dolayısıyla veri aktarımının güvenliği için master secrethayati önem taşır.

Master Secret‘ın oluşturulmasını calculate_master_secret(client_random, server_random, pre_master_secret)şeklinde ifade edebililriz. Amacımız iki tarafta da aynı master secret ı oluşturmak olduğundan, client ve server birbirlerine rasgele oluşturulmuş veriler yollar. Böylece her bağlantıda farklı bir master oluşturulması sağlanır. Üçüncü parametre, pre_master_secret ise master_secret’ın yalnızca client ve server tarafından oluşturulmasını sağlar. Bu noktada bir adım daha ileri gidip pre_master_secret’ı aramamız gerekiyor.

PreMaster Secret (PMS); client tarafından oluşturulan, master secret‘ın gizliliğini temin eden 48 byte’lık bir şifre. Client PMS’i oluşturduğunda (4), Master Secret için gereken tüm veriler elinde hazır olmuş oluyor. Ancak PMS‘in güvenli bir şekilde server’a iletilmesi lazım. Bunun için de public-key cryptography(PKC) kullanılıyor. 2’de server’dan alınan sertifikanın içindeki public key kullanılarak şifrelenenen PMS, server’a gönderiliyor ve bu aşamadan sonra şifrelenerek gönderilen bütün verilerin güvenliği, bu şifrelenmiş PMS‘in yanlış ellere ulaşamayacağı varsayımına dayanmış oluyor. Biz de tam bu noktada araya giriyoruz!

Public-key algoritmalarının temeli şuna dayanır: Bir veriyi herkes bir public-key kullanarak şifreleyebilir. Dolayısıyla public-key’in gizliliği önemli değildir. Ama bu şifrelenmiş veri yalnızca o public-key’e karşılık gelen private-key tarafından çözülebilir. Yani server’ın sertifikasında kullandığı public-key’e karşılık gelen private-key elimizdeyse, önce pre-master-secret’ı, sonra master-secret’ı daha sonra da application data flow sırasında gereken tüm şifreleri hesaplamamız mümkün demektir.

Tabi facebook, gmail gibi şirketler kolayca private-key lerini paylaşmayacaktır. Dolayısıyla bu yöntemin kullanılabilirliği tartışılır.
Peki ya client’a gelen sertifikadaki public-key aslında facebook’un değil, bizim private-key’imize ait public-key ise??

sıra geldi MITM’e..

Salih AHİ

Boost Program Options Kütüphanesi ve Kullanımı

Boost.Program Options kütüphanesini bu makalede elimizden geldiğince tanıtmaya çalışıcağım. Faydalı olması dileğiyle..

Nedir ?

Programlarımızın başlaması için ihtiyaç duyduğu değerleri konfigürasyon dosyası veya komut satırından alınmasını sağlayan esnek ve kullanışlı bir kütüphanedir. Verileri isim, değer şeklinde ikililer şeklinde muhafaza eder.

Kullanımı
Program Options kütüphanesinin kullanımı oldukça basittir. C++ diline aşina birinin rahatlıkla kullanabileceği bir kütüphanedir. Öncelikle derleme aşamasında sentaks hatası olmaması için

#include <boost/program_options.hpp>

   

şeklinde başlık dosyasını dahil etmemiz gerekiyor.

boost::program_options isim alanı için bundan sonra po kısaltmasını kullanacağım.

Öncelikle oluşturacağımız yapıyı tanımlamamız gerekiyor. Örneğin ;

po::options_description cmdline(“Command Line Options”);

  

po isim alanı içerisinde options_description sınıfı türünden cmdline isminde bir nesne (instance) oluşturuyoruz. Bu sınıfın yapılandırıcı işlevine (bundan sonra ctor diyeceğim) geçtiğimiz string ise bu nesnenin varoluş nedenini belirten bir açıklama. İsminden ve açıklamasından anlaşılacağı üzere bu değişkeni komut satırı argümanlarını almak için kullanacağız.

Şimdi sıra alacağımız parametreleri belirlemeye geldi. Bunun için po::options_description sınıfı içerisindeki add_options fonksiyonunu kullanacağız. Bu fonksiyonun geri dönüş değeri options_description_easy_init sınıfı türünden bir nesne bu nesnede içsel olarak overload edilmiş operator() fonksiyonlarını çağırarak oluşturduğumuz argümanları sınıfa geçer. Evet biraz kulağa karışık geliyor ama C++ dilinin en güzel yanlarından biride programcıya bu kadar geniş ve esnek bir yapı sunmasıdır. Örneği inceleyelim :

    string configFile;
    cmdline.add_options()
    	(“help,h”, “produce help message”)
    	(“version,v”, “program version”)
     	(“config,c”, po::value<string>(&configFile)->default_value(“/usr/local/etc/cppturkey.cfg”),    ”configuration file path”);

Verilen birinci argümanda help ve h birinci parametre olarak geçilmiş. İkinci parametre ise birinci parametrenin açıklaması. İlk iki fonksiyon çağrısı birbirinin aynısı olduğundan o yüzden direkt üçüncüye geçiyorum.

Burda dikkat ederseniz farklı bir operator() fonksiyonu çağrılmış. Burda yine 1. ve 3. parametrelerin işlevleri aynı. Fakat 2.parametre alınan argüman daha önce oluşturulan bir string değişkenine atanmış, bu da po içerisindeki bir template aracılığyla gerçekleşmiş. Bu değişkene default olarakta bir değer atayabilirsiniz yukarıda “/usr/local/etc/cppturkey.cfg” atandığı gibi.

Şimdi sıra geldi yapılan bu tanımlardan sonra verileri almaya ve onları (değişken, değer) şeklinde kaydetmeye. Aşağıdaki bölümü inceleyelim :

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, cmdline), vm);
    po::notify(vm);

Yukarıda variables_map türünden vm adında bir nesne oluşturuluyor. Aslında bu sınıf std::map sınıfından türetilerek özelleştirilmiş bir pair(ikili) yapısıdır. Alt satırda ise komut satırı argümanları parse edilerek bu yapıya kaydediliyor. notify fonksiyonu da vm içerisindeki tüm nesneler için notify fonksiyonunu çağırıyor, böylelikle eger gerekliyse değer normak değişkene aktarılabiliyor.

Şimdi config dosyasından okumaya bir göz atalım. Aslında benzer çok yönü var o yüzden biraz hızlı geçicez. Bizim okuyacağımız dosyalar .ini tarzı dosyalardır. Bölüm başlıkları [ ] içinde belirtilmeli sonra ise yine değişken ve değer ikilileri gelmelidir.

[CPPTURKEY]
writer=islamyasar
gibi ..

Böylelike writer değişkenine CPPTURKEY.writer şeklinde hiyararşik şekilde erişebiliriz.

Örneğe geçelim :

    po::options_description configoptions(“Configuration Options”);
    configoptions.add_options()
                   (“CPPTURKEY.writer”, po::value<string>(&cppturkey), “CPP User Group Turkey”);

Örnekte de görüldüğü gibi aynı şekilde nesnelerimiz oluşturuyoruz. Yalnız burda CPPTURKEY.writer kısmında hiyararşik bir şekilde nesneyi belirtmeye dikkat edelim.

Okuma kısmı biraz farklılık gösteriyor komut satırı argümanlarından :

    ifstream ifs(“/usr/local/etc/cppturkey.cfg”);
    if(!ifs)
   	 cout << “can not open config file: ” << endl;
    else  {
    
    	 po::store(po::parse_config_file(ifs, configoptions), vm);
   	 po::notify(vm);
    }

Gördüğünüz gibi ifstream nesnesi oluşturarak dosyayı okuyoruz. Daha sonra ise yine parse_config_file‘ın başka bir overload’ına bu nesneyi ve daha önce oluşturduğumuz configoptions nesnesini parametre olarak veriyoruz.

Şimdi geldi bu değerleri kullanmaya örnekleri inceleyelim :

    if(vm.count(“help”)){
    	cout  « “size yardima her zaman haziriz. cppturkey.com :))” « endl;
    	return 0;
    }

    if(vm.count(“version”)) {
    	cout « “Boostla olan seruvenimiz devam edecek” « endl;
    	return 0;
    }

örneklerde olduğu gibi version parametresi atandıysa deyim işletilecek ve “Boostla olan seruvenimiz devam edecek” stringi ekrana verilecek.

Bunlar haricinde vm yapısının içerisindeki verilere :

vm[“CPPTURKEY.writer”].as() şeklinde de ulaşabilirsiniz bu ifade vm içerisindeki CPPTURKEY.writer’a atanmış değeri (mapped value) string olarak döndürecektir.

Islam Yaşar

Private key kullanarak https çözümleme – 1

Amaç: https üzerinden şifreli olarak aktarılan verilerin içeriğini görebilmek.

Gerekli programlar: openssl, viewssld, libdssl

Burada network ayarları ile uğraşmamak için trafiği çözen program ve web sunucusunu aynı cihaz üzerinden çalıştırıyoruz. Veriyi çözmek için kullandığımız viewssld programının conf dosyalarında gerekli ayarlar yapılarak bu iki programın farklı cihazlardan çalışması da mümkün.

 

Öncelikle openssl kullanarak sertifika ve key’leri oluşturuyorup web sunucuyu başlatıyoruz
> openssl req -x509 -nodes -newkey rsa:1024 -keyout testkey.pem -out testcert.pem
 
> openssl s_server -key /home/testkey.pem -cert /home/testcert.pem -WWW -cipher RC4-SHA -accept 443

ardından bu komutu çalıştırdığımız klasörde test için bir dosya oluşturuyoruz
> echo “test data” > test.html

 

Yukarıdaki linkten viewssld programını indirip derledikten sonra conf dosyasını şu şekilde ayarlıyoruz:

# example configuration file for viewssld
# PID-file path (default: /var/run/viewssld.pid)
#pid = /var/run/viewssld.pid
# daemonize? on/off (default: off)
#daemon = off
# loglevel 0-10 (default: 0)
loglevel = 10
 
# server 2 configuration
[server2]
src = eth0
dst = eth0
ip = 192.168.21.128
port = 443
key = /home
/testkey.pem
dsslport = 80

 

şimdi wireshark’ı başlatıp firefoxtan “https:/ip/test.html” adresine girdiğimizde “Test Data” içerikli sayfayı görebiliyor olmamız lazım. Bakalım wireshark’ta neler geliyor:

 image

üstteki resimde sayfa request’ini açık bir get request olarak görebiliyoruz. Aşağıda da buna verilen cevap var. Full response gelmemiş olsa da verilerin cleartext olarak yollandığını görebiliyoruz.

image

Peki ne ara çözüldü bu veriler?

Cevabı çok yakında..

Salih AHİ

ipv6 adresleri için bir network mask fonksiyonu implementasyonu

Geçenlerde şirkette ürünlerimizi ipv6 içinde uygun hale geitrmek için birtakım çalışmalar içindeydik. Takdir edersinizki ipv6 adresleri 16 byte yani 128 bit. Bu sebepten ötürü C veya C++ da primitiv (ilkel) türlere sığmayacak büyüklükte (int, double, float..). İşte bütün bunlardan ötürü bi ipv6 adresine network mask yeni adıyla prefix uygulamak eskisi kadar kolay değil. Ama zorda değil 😀

Ben bir tane işimi görmesi için bir fonksiyon yazdım bunu şimdilik sizinle paylaşıyorum. Aklımda ise kapsamlı bir ipv6 class ’ ı oluşturmak var ..

  
typedef u_int16_t ip6_addr[8]; // for ipv6 addresses

void ipv6_app_mask(const char *ip6addr, unsigned int mask, ip6_addr ip6){

            ip6_addr in_ip6;

           if(inet_pton(PF_INET6, ip6addr, ip6) &lt; 0)

                        cout « “ipv6 adresi gecersiz” « endl;

            for(int i = 0; i &lt; 8; i++){

            in_ip6[i] = ntohs(ip6[i]);

             }

             int index = (int) (mask / 16);

            int remain_mask = mask % 16;

            if(remain_mask == 0 &amp;&amp; index == 8)

            return;

            switch(remain_mask){

            case 0:in_ip6[index++] = 0; break;

            case 1:in_ip6[index++]&amp;=0x8000; break;

            case 2:in_ip6[index++]&amp;=0xc000; break;

            case 3:in_ip6[index++]&amp;=0xe000; break;

            case 4:in_ip6[index++]&amp;=0xf000; break;

            case 5:in_ip6[index++]&amp;=0xf800; break;

            case 6:in_ip6[index++]&amp;=0xfc00; break;

            case 7:in_ip6[index++]&amp;=0xfe00; break;

            case 8:in_ip6[index++]&amp;=0xff00; break;

            case  9:in_ip6[index++]&amp;=0xff80; break;

            case 10:in_ip6[index++]&amp;=0xffc0; break; 

            case 11:in_ip6[index++]&amp;=0xffe0; break;

            case 12:in_ip6[index++]&amp;=0xfff0; break;

            case 13:in_ip6[index++]&amp;=0xfff8; break;

            case 14:in_ip6[index++]&amp;=0xfffc; break;

           case 15:in_ip6[index++]&amp;=0xfffe; break;

     }

         for (int i = index; i &lt; 8; i++){

              in_ip6[i] = 0;

        }

        for(int i = 0; i &lt; 8; i++){

             ip6[i] = htons(in_ip6[i]);

        }

   return;

}

İslam YAŞAR

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İ