← Kursa Dön
📄 Text · 20 min

Fixed-width Integers, size_t ve Numeral Systems

C++'ta int yazdığında aklına "4 byte tam sayı" geliyordur. Ama gerçek bu kadar basit değil. int'in boyutu platforma göre değişebilir — evet, aynı kod farklı sistemlerde farklı davranabilir. Bu derste, bu belirsizliğe son veren sabit genişlikli tam sayıları (fixed-width integers), gizemli size_t türünü ve sayıların farklı gösterim biçimlerini (numeral systems) öğreneceksin.

Eğer bir gün gömülü sistem, ağ programlama veya oyun motoru geliştirme yapacaksan, bu konular seni çok yakından ilgilendirecek. Ama düz masaüstü uygulamalar yazsan bile, bu bilgiler kodunu daha sağlam ve taşınabilir (portable) yapar.


int Boyutu Neden Değişir?

C++ standardı, int'in boyutunu kesin olarak tanımlamaz. Sadece minimum gereksinimleri belirtir:

TürMinimum BoyutTipik Boyut (x86-64)
char1 byte1 byte
short2 byte2 byte
int2 byte4 byte
long4 byte4 veya 8 byte
long long8 byte8 byte

Evet, int minimum 2 byte! Modern masaüstü sistemlerde hep 4 byte olsa da, gömülü sistemlerde veya eski platformlarda 2 byte olabilir. long ise daha da kafa karıştırıcı: Windows'ta 4 byte iken Linux'ta 8 byte'tır (64-bit sistemlerde).

Bunu şöyle düşün: kargo kutusu sipariş ediyorsun. "Orta boy kutu" diyorsun ama farklı kargo firmaları "orta boy"u farklı anlıyor. Birinde 30x30cm, diğerinde 40x40cm. İçine koyacağın şey 35cm ise, birinde sığar, diğerinde sığmaz. İşte int de böyle — "orta boy tam sayı" demek, ama tam boyut platforma bağlı.

#include <iostream>

int main() {
    std::cout << "char:      " << sizeof(char)      << " byte" << std::endl;
    std::cout << "short:     " << sizeof(short)     << " byte" << std::endl;
    std::cout << "int:       " << sizeof(int)       << " byte" << std::endl;
    std::cout << "long:      " << sizeof(long)      << " byte" << std::endl;
    std::cout << "long long: " << sizeof(long long) << " byte" << std::endl;

    // Bu çıktı platformdan platforma DEĞİŞEBİLİR!
    return 0;
}

Bu belirsizlik, C'nin 1970'lerde PDP-11 gibi çok farklı mimarilere uyum sağlama ihtiyacından doğdu. Derleyiciye esneklik tanımak, o dönemde mantıklıydı. Ama bugün, özellikle veri serileştirme (serialization), ağ protokolleri ve dosya formatları yazarken, kesin boyutlara ihtiyacın var.


Fixed-width Integers: Kesin Boyut Garantisi

C++11 ile gelen <cstdint> header'ı, boyutu kesin olan tam sayı türlerini tanımlar. Bu türler, tüm platformlarda aynı boyutta olma garantisi verir.

Temel Türler

#include <cstdint>
#include <iostream>

int main() {
    // İşaretli (signed) — negatif değer tutabilir
    int8_t   a = -128;            // Tam 1 byte (-128 ile 127 arası)
    int16_t  b = -32768;          // Tam 2 byte
    int32_t  c = -2147483648;     // Tam 4 byte
    int64_t  d = -9223372036854775807; // Tam 8 byte

    // İşaretsiz (unsigned) — sadece pozitif değerler
    uint8_t  e = 255;             // Tam 1 byte (0 ile 255 arası)
    uint16_t f = 65535;           // Tam 2 byte
    uint32_t g = 4294967295;      // Tam 4 byte
    uint64_t h = 18446744073709551615ULL; // Tam 8 byte

    std::cout << "int8_t:  " << sizeof(int8_t)  << " byte" << std::endl;  // 1
    std::cout << "int16_t: " << sizeof(int16_t) << " byte" << std::endl;  // 2
    std::cout << "int32_t: " << sizeof(int32_t) << " byte" << std::endl;  // 4
    std::cout << "int64_t: " << sizeof(int64_t) << " byte" << std::endl;  // 8

    return 0;
}

İsimlendirme Mantığı

İsimler çok sistematik:

  • int → tam sayı (integer)

  • 8, 16, 32, 64 → bit cinsinden boyut

  • _t → type (tür) anlamında sonek

  • u öneki → unsigned (işaretsiz)

Yani uint32_t = "unsigned integer, 32 bit" = "işaretsiz 32-bit tam sayı" = tam 4 byte.

Diğer Varyantlar

<cstdint> sadece sabit boyutlu türler sunmaz, birkaç varyant daha vardır:

#include <cstdint>

// "En az" bu kadar geniş — ama daha geniş olabilir
int_least8_t  x;  // En az 8 bit
int_least16_t y;  // En az 16 bit
int_least32_t z;  // En az 32 bit

// "En hızlı" en az bu kadar geniş olan tür
int_fast8_t   a;  // En hızlı, en az 8 bit
int_fast16_t  b;  // En hızlı, en az 16 bit
int_fast32_t  c;  // En hızlı, en az 32 bit

// Pointer boyutunda tam sayı
intptr_t  ptr_val;   // Pointer tutabilecek kadar geniş (signed)
uintptr_t uptr_val;  // Pointer tutabilecek kadar geniş (unsigned)

// Mümkün olan en geniş tam sayı
intmax_t  biggest;   // En az 64 bit
uintmax_t ubiggest;

int_fast türleri ilginçtir: derleyici, o platformda en hızlı işlenecek türü seçer. Mesela bazı 64-bit platformlarda int_fast16_t aslında 64-bit olabilir, çünkü işlemci 64-bit değerlerle daha hızlı çalışır.

💡 İpucu: Günlük kodda genellikle int32_t ve int64_t yeterli. int_fast ve int_least varyantları daha çok kütüphane geliştiricileri ve performans-kritik sistemler için. Eğer ne kullanacağını bilmiyorsan, genel amaçlı işler için int ve boyut önemli olduğunda int32_t / int64_t kullan.

Ne Zaman Fixed-width Kullanmalı?

DurumTercih
Genel amaçlı sayaç, döngü değişkeniint yeterli
Dosya formatı, ağ protokolüint32_t, uint16_t vb. — kesin boyut şart
Gömülü sistem, bellek kısıtlıint8_t, uint8_t — her byte önemli
Çok büyük sayılarint64_t
Dizin, boyutsize_t (birazdan göreceğiz)
Pixel, renk değeriuint8_t (0-255 aralığı)
Bit manipülasyonuuint32_t, uint64_t — unsigned tercih et

size_t: Boyut ve İndeks Türü

size_t, C++'ta boyut ve indeks değerleri için kullanılan özel bir türdür. İşaretsizdir (unsigned) ve platformun adres alanını temsil edebilecek kadar geniştir.

  • 32-bit sistemde: size_t = 4 byte (unsigned, 0 – ~4.2 milyar)

  • 64-bit sistemde: size_t = 8 byte (unsigned, 0 – ~18.4 kentilyon)

Neden özel bir tür? Çünkü bir dizinin boyutu veya bir indeks negatif olamaz. Ve teorik olarak, bellekteki en büyük nesne kadar büyük bir değeri ifade edebilmesi gerekir. int her zaman bunu garanti edemez — 32-bit int en fazla ~2 milyar tutabilir, ama bir 64-bit sistemde bellek bundan çok daha büyük olabilir.

#include <iostream>
#include <vector>
#include <string>
#include <cstddef>  // size_t için (çoğu header zaten dahil eder)

int main() {
    std::vector<int> numbers = {10, 20, 30, 40, 50};

    // size() fonksiyonu size_t döndürür
    size_t boyut = numbers.size();
    std::cout << "Eleman sayisi: " << boyut << std::endl;

    // Döngülerde size_t kullanımı
    for (size_t i = 0; i < numbers.size(); ++i) {
        std::cout << "numbers[" << i << "] = " << numbers[i] << std::endl;
    }

    // String boyutu da size_t
    std::string mesaj = "Merhaba Dunya";
    size_t uzunluk = mesaj.length();
    std::cout << "String uzunlugu: " << uzunluk << std::endl;

    // sizeof operatörü de size_t döndürür
    size_t intBoyut = sizeof(int);
    std::cout << "int boyutu: " << intBoyut << " byte" << std::endl;

    return 0;
}

size_t ve int Karıştırma Tehlikesi

size_t unsigned (işaretsiz) olduğu için, int ile karıştırmak uyarılara ve hatalara yol açabilir:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // UYARI: signed/unsigned karşılaştırması
    for (int i = 0; i < vec.size(); ++i) {  // vec.size() → size_t, i → int
        std::cout << vec[i] << " ";
    }

    // DOĞRU: size_t kullan
    for (size_t i = 0; i < vec.size(); ++i) {
        std::cout << vec[i] << " ";
    }

    // VEYA: Modern C++ — range-based for
    for (const auto& val : vec) {
        std::cout << val << " ";
    }

    return 0;
}

⚠️ Dikkat: size_t ile geriye doğru sayarken dikkat et! size_t negatif olamaz, bu yüzden i >= 0 koşulu HER ZAMAN doğrudur (unsigned bir değer her zaman >= 0'dır). Bu sonsuz döngüye neden olur:

std::vector<int> vec = {1, 2, 3, 4, 5};

// BUG: Sonsuz döngü! size_t asla negatif olmaz
for (size_t i = vec.size() - 1; i >= 0; --i) {
    std::cout << vec[i] << " ";
    // i = 0 olunca, --i onu 18446744073709551615 yapar (wraparound)!
}

// Doğru yol 1: index'i 1'den başlat
for (size_t i = vec.size(); i > 0; --i) {
    std::cout << vec[i - 1] << " ";
}

// Doğru yol 2: signed tür kullan
for (int i = static_cast<int>(vec.size()) - 1; i >= 0; --i) {
    std::cout << vec[i] << " ";
}

// Doğru yol 3: C++20 ssize()
// for (auto i = std::ssize(vec) - 1; i >= 0; --i) { ... }

Sayı Sistemleri (Numeral Systems)

Bilgisayarlar binary (ikili) sistemde çalışır — ama biz insanlar onluk (decimal) sisteme alışkınız. C++ bize sayıları farklı tabanlarda yazma imkânı verir: decimal, binary, octal ve hexadecimal.

Bunu şöyle düşün: aynı mesafe "5 kilometre", "5000 metre" veya "500000 santimetre" olarak ifade edilebilir. Hepsi aynı şey, farklı birimlerle. Sayı tabanları da böyle — aynı değer, farklı gösterim.

Decimal (Onluk — Taban 10)

Günlük hayatta kullandığımız sistem. 0-9 rakamlarını kullanır. C++'ta varsayılan budur.

int sayi = 42;           // Decimal: 42
int buyuk = 1000000;     // Bir milyon
int ayirici = 1'000'000; // C++14: rakam ayırıcı ile aynı şey, ama daha okunaklı

C++14'te gelen rakam ayırıcı (') tamamen görsel bir kolaylık — derleyici görmezden gelir. 1'000'000, 1000000 ile aynı şeydir.

Binary (İkili — Taban 2)

Sadece 0 ve 1 kullanır. 0b veya 0B önekiyle yazılır (C++14).

int flags   = 0b00001111;  // Binary: 15 (decimal)
int mask    = 0b1010;      // Binary: 10 (decimal)
int byte_ex = 0b1111'1111; // 255 — ayırıcı ile okunaklı

// Her pozisyon 2'nin kuvveti:
// 0b1010 = 1*8 + 0*4 + 1*2 + 0*1 = 10

Binary, bit manipülasyonu yaparken çok faydalı. Hangi bitin açık, hangisinin kapalı olduğunu doğrudan görürsün.

Octal (Sekizlik — Taban 8)

0-7 rakamlarını kullanır. 0 önekiyle yazılır.

int dosyaIzni = 0755;    // Octal: 493 (decimal)
int sayi      = 077;     // Octal: 63 (decimal)

// Pozisyonlar 8'in kuvvetleri:
// 077 = 0*64 + 7*8 + 7*1 = 63

Octal, çoğunlukla Unix dosya izinleri (chmod 755) ile karşına çıkar. Bunun dışında modern C++'ta pek kullanılmaz.

⚠️ Dikkat: Sayının başına sıfır koymak onu octal yapar! Bu çok yaygın bir tuzaktır:

int a = 10;   // Decimal: 10
int b = 010;  // OCTAL: 8!  (1*8 + 0*1)
int c = 0010; // OCTAL: 8!

// Hizalama için başa sıfır koyma alışkanlığın varsa dikkat:
int aylar[] = {
    01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12
    //                              ^^ HATA! 08 ve 09 geçersiz octal!
};

08 ve 09 geçersiz octal sayılardır (octal'de sadece 0-7 rakamları var). Derleyici hata verir. Bu yüzden sayıların başına gereksiz sıfır koyma!

Hexadecimal (On Altılık — Taban 16)

0-9 ve A-F (veya a-f) kullanır. 0x veya 0X önekiyle yazılır.

int renk     = 0xFF5733;     // Hex: 16734003 (turuncu renk kodu)
int adres    = 0xDEADBEEF;   // Hex: klasik debug değeri
int byte_max = 0xFF;          // Hex: 255
int word     = 0xFFFF;        // Hex: 65535

// A=10, B=11, C=12, D=13, E=14, F=15
// 0xFF = 15*16 + 15*1 = 255
// 0x1A = 1*16 + 10*1 = 26

Hexadecimal, bellekle çalışırken vazgeçilmezdir. Her hex rakamı tam 4 bit'e karşılık gelir, bu yüzden byte değerlerini temsil etmek için mükemmeldir:

  • 1 byte = 2 hex rakamı (0xFF)

  • 2 byte = 4 hex rakamı (0xFFFF)

  • 4 byte = 8 hex rakamı (0xFFFFFFFF)

Karşılaştırma Tablosu

#include <iostream>

int main() {
    int sayi = 255;

    std::cout << "Decimal:     " << std::dec << sayi << std::endl;  // 255
    std::cout << "Octal:       " << std::oct << sayi << std::endl;  // 377
    std::cout << "Hexadecimal: " << std::hex << sayi << std::endl;  // ff

    // Önekli gösterim için
    std::cout << "Hex (onek):  0x" << std::hex << sayi << std::endl;  // 0xff

    // Tekrar decimal moda dön
    std::cout << std::dec;

    return 0;
}

Binary çıktı için std::bitset kullanılır (bit manipulation dersinde detaylıca göreceğiz):

#include <iostream>
#include <bitset>

int main() {
    int sayi = 255;
    std::cout << "Binary: " << std::bitset<8>(sayi) << std::endl;  // 11111111
    std::cout << "Binary: " << std::bitset<16>(sayi) << std::endl; // 0000000011111111
    return 0;
}

Literal Suffixes (Sabit Sonekleri)

Bir sayıyı doğrudan koda yazdığında (literal), derleyici onun türünü belirlemek zorundadır. Varsayılan olarak tam sayılar int, ondalıklılar double olur. Ama bazen farklı bir tür isteyebilirsin — suffix'ler bunun için var.

Tam Sayı Suffix'leri

auto a = 42;       // int
auto b = 42u;      // unsigned int
auto c = 42l;      // long
auto d = 42ul;     // unsigned long
auto e = 42ll;     // long long
auto f = 42ull;    // unsigned long long

// Büyük/küçük harf fark etmez ama L tercih edilir (l ile 1 karışır)
auto g = 42ULL;    // unsigned long long
auto h = 42LL;     // long long

Ondalık Sayı Suffix'leri

auto x = 3.14;     // double (varsayılan)
auto y = 3.14f;    // float
auto z = 3.14L;    // long double

// Bilimsel gösterim (scientific notation)
auto buyuk = 1.5e6;    // double: 1500000.0
auto kucuk = 2.3e-4f;  // float: 0.00023

Neden Önemli?

// SORUN: Tamsayı taşması (overflow)
auto sonuc1 = 1000000 * 1000000;  // int * int → int → TAŞMA!
// int max ~2.1 milyar, 1 trilyon sığmaz

// ÇÖZÜM: Suffix ile doğru türü belirt
auto sonuc2 = 1000000LL * 1000000LL;  // long long * long long → long long → OK

// SORUN: float hassasiyeti
double pi = 3.14159265358979;     // Tam hassasiyet (double)
float  pi_f = 3.14159265358979f;  // Float hassasiyetinde (~7 basamak)

// Karışık tipler
auto result = 5 / 2;     // int / int = 2 (tam sayı bölmesi!)
auto result2 = 5.0 / 2;  // double / int = 2.5 (ondalık bölme)
auto result3 = 5 / 2.0f; // int / float = 2.5f

💡 İpucu: Özellikle büyük sayılarla çarpma yaparken suffix kullanmayı unutma. int * int çarpımı taşabilir ve undefined behavior'a yol açabilir. 1000 * 1000 * 1000 gibi ifadelerde en az bir operandı LL suffix'iyle long long yap.


sizeof Operatörü

sizeof, bir türün veya değişkenin bellekte kaç byte yer kapladığını döndürür. Derleme zamanında (compile-time) hesaplanır, yani çalışma zamanında performans maliyeti yoktur.

#include <iostream>
#include <cstdint>

int main() {
    // Türler üzerinde
    std::cout << "bool:      " << sizeof(bool)      << " byte" << std::endl;
    std::cout << "char:      " << sizeof(char)      << " byte" << std::endl;
    std::cout << "int:       " << sizeof(int)       << " byte" << std::endl;
    std::cout << "float:     " << sizeof(float)     << " byte" << std::endl;
    std::cout << "double:    " << sizeof(double)    << " byte" << std::endl;
    std::cout << "int64_t:   " << sizeof(int64_t)   << " byte" << std::endl;
    std::cout << "pointer:   " << sizeof(int*)      << " byte" << std::endl;

    return 0;
}

Dizilerle sizeof

#include <iostream>

int main() {
    int dizi[10];

    std::cout << "Dizinin toplam boyutu: " << sizeof(dizi) << " byte" << std::endl;
    // 10 * 4 = 40 byte (int 4 byte ise)

    std::cout << "Bir elemanin boyutu: " << sizeof(dizi[0]) << " byte" << std::endl;
    // 4 byte

    // Eleman sayısını hesaplama (klasik C trick)
    std::cout << "Eleman sayisi: " << sizeof(dizi) / sizeof(dizi[0]) << std::endl;
    // 40 / 4 = 10

    // C++17 ile daha güvenli yol:
    // std::size(dizi) → 10
    return 0;
}

Struct/Class ile sizeof

#include <iostream>
#include <cstdint>

struct Compact {
    uint8_t  a;  // 1 byte
    uint8_t  b;  // 1 byte
    uint16_t c;  // 2 byte
};

struct Padded {
    uint8_t  a;  // 1 byte + 3 byte padding
    uint32_t b;  // 4 byte
    uint8_t  c;  // 1 byte + 3 byte padding
};

struct Reordered {
    uint32_t b;  // 4 byte
    uint8_t  a;  // 1 byte
    uint8_t  c;  // 1 byte + 2 byte padding
};

int main() {
    std::cout << "Compact:   " << sizeof(Compact)   << " byte" << std::endl;  // 4
    std::cout << "Padded:    " << sizeof(Padded)    << " byte" << std::endl;  // 12!
    std::cout << "Reordered: " << sizeof(Reordered) << " byte" << std::endl;  // 8

    return 0;
}

Padded'ın 6 byte yerine 12 byte olmasının sebebi alignment (hizalama). İşlemciler, verilerin belirli adreslerde hizalanmasını ister. uint32_t 4'ün katı adreste olmalıdır, bu yüzden derleyici aralara "padding" (dolgu) byte'ları ekler. Üyelerin sırasını değiştirmek (Reordered) padding'i azaltabilir.


Portability (Taşınabilirlik) Tuzakları ve Best Practices

1. int Boyutuna Güvenme

// KÖTÜ: int'in 4 byte olduğunu varsayıyor
char buffer[4];
memcpy(buffer, &myInt, 4);  // int 2 byte ise? BOOM!

// İYİ: sizeof kullan
char buffer[sizeof(int)];
memcpy(buffer, &myInt, sizeof(int));

// DAHA İYİ: Fixed-width kullan
int32_t myValue = 42;
char buffer[sizeof(int32_t)];  // Her zaman 4 byte
memcpy(buffer, &myValue, sizeof(int32_t));

2. Signed/Unsigned Karıştırma

// KÖTÜ: Signed/unsigned karşılaştırma
int i = -1;
unsigned int u = 1;
if (i < u) {
    std::cout << "-1 < 1" << std::endl;
} else {
    std::cout << "-1 >= 1 ???" << std::endl;  // BU ÇALIŞIR!
}
// -1 unsigned'a dönüşür → çok büyük pozitif sayı → -1 >= 1 olur!

3. Narrowing Conversion

int64_t buyuk = 3000000000LL;  // 3 milyar
int32_t kucuk = buyuk;          // Veri kaybı! (narrowing)

// C++11 brace initialization bu hatayı yakalar:
// int32_t kucuk2{buyuk};  // DERLEME HATASI!

// Bilinçli dönüşüm (intentional):
int32_t kucuk3 = static_cast<int32_t>(buyuk);  // "Biliyorum, isteyerek yapıyorum"

4. Karakter ve Tam Sayı Karışıklığı

#include <iostream>
#include <cstdint>

int main() {
    int8_t sayi = 65;
    std::cout << sayi << std::endl;  // "A" yazdırır! (char gibi davranır)

    // Çünkü int8_t genellikle signed char'ın typedef'idir
    // Sayı olarak yazdırmak için:
    std::cout << static_cast<int>(sayi) << std::endl;  // "65" yazdırır

    uint8_t sayi2 = 66;
    std::cout << sayi2 << std::endl;         // "B" yazdırır
    std::cout << +sayi2 << std::endl;        // "66" yazdırır (unary + trick)
    std::cout << static_cast<int>(sayi2) << std::endl;  // "66" yazdırır

    return 0;
}

Bu tuzak özellikle int8_t ve uint8_t ile sık karşılaşılır. std::cout'a bunları verdiğinde, char gibi yorumlar ve karakter yazdırır. Sayı olarak görmek istiyorsan cast gerekir.


std::numeric_limits

<limits> header'ındaki std::numeric_limits sınıfı, her sayısal türün minimum, maksimum ve diğer özelliklerini sorgulamana olanak verir. Sabit kodlanmış (hardcoded) sınır değerleri yazmak yerine bunu kullan.

#include <iostream>
#include <limits>
#include <cstdint>

int main() {
    std::cout << "=== int ===" << std::endl;
    std::cout << "Min: " << std::numeric_limits<int>::min() << std::endl;
    std::cout << "Max: " << std::numeric_limits<int>::max() << std::endl;
    std::cout << "Bit: " << std::numeric_limits<int>::digits << std::endl;
    std::cout << "Signed: " << std::numeric_limits<int>::is_signed << std::endl;

    std::cout << "\n=== uint32_t ===" << std::endl;
    std::cout << "Min: " << std::numeric_limits<uint32_t>::min() << std::endl;  // 0
    std::cout << "Max: " << std::numeric_limits<uint32_t>::max() << std::endl;  // 4294967295

    std::cout << "\n=== double ===" << std::endl;
    std::cout << "Min: " << std::numeric_limits<double>::min() << std::endl;
    std::cout << "Max: " << std::numeric_limits<double>::max() << std::endl;
    std::cout << "Epsilon: " << std::numeric_limits<double>::epsilon() << std::endl;
    std::cout << "Infinity: " << std::numeric_limits<double>::infinity() << std::endl;
    std::cout << "Hassasiyet: " << std::numeric_limits<double>::digits10 << " basamak" << std::endl;

    return 0;
}

Pratik Kullanım: Taşma Kontrolü

#include <iostream>
#include <limits>
#include <cstdint>

bool safeAdd(int32_t a, int32_t b, int32_t& result) {
    // Toplama taşma kontrolü
    if (b > 0 && a > std::numeric_limits<int32_t>::max() - b) {
        return false;  // Pozitif taşma olurdu
    }
    if (b < 0 && a < std::numeric_limits<int32_t>::min() - b) {
        return false;  // Negatif taşma olurdu
    }
    result = a + b;
    return true;
}

int main() {
    int32_t sonuc;

    if (safeAdd(2000000000, 1000000000, sonuc)) {
        std::cout << "Sonuc: " << sonuc << std::endl;
    } else {
        std::cout << "TASMA! Sonuc int32_t'ye sigmaz." << std::endl;
    }

    if (safeAdd(100, 200, sonuc)) {
        std::cout << "Sonuc: " << sonuc << std::endl;  // 300
    }

    return 0;
}

Sentinel Değerler

#include <limits>
#include <cstdint>

// "Geçersiz" veya "bulunamadı" anlamında sentinel değerler
constexpr int32_t INVALID_ID = std::numeric_limits<int32_t>::min();
constexpr uint32_t NOT_FOUND = std::numeric_limits<uint32_t>::max();
constexpr double UNDEFINED   = std::numeric_limits<double>::quiet_NaN();

int32_t findPlayer(const std::string& name) {
    // ... arama kodu ...
    return INVALID_ID;  // Bulunamadı
}

sizeof vs numeric_limits — Fark Ne?

Özelliksizeofnumeric_limits
Ne döndürür?Byte cinsinden boyutMin, max, özellikler
Kullanımsizeof(int) → 4numeric_limits<int>::max() → 2147483647
Derleme zamanı?EvetEvet (constexpr)
Tür kısıtıHerhangi bir türSayısal türler

İkisi farklı soruları cevaplar: sizeof "ne kadar yer kaplıyor?", numeric_limits "ne kadar büyük/küçük olabilir?" sorusuna yanıt verir.


Tam Bir Örnek: Tür Bilgi Raporu

Tüm konuları bir araya getiren bir program yazalım:

#include <iostream>
#include <limits>
#include <cstdint>
#include <bitset>

template <typename T>
void printTypeInfo(const std::string& name) {
    std::cout << "--- " << name << " ---" << std::endl;
    std::cout << "  Boyut:    " << sizeof(T) << " byte ("
              << sizeof(T) * 8 << " bit)" << std::endl;

    if constexpr (std::numeric_limits<T>::is_integer) {
        std::cout << "  Signed:   "
                  << (std::numeric_limits<T>::is_signed ? "evet" : "hayir")
                  << std::endl;
        std::cout << "  Min:      " << +std::numeric_limits<T>::min() << std::endl;
        std::cout << "  Max:      " << +std::numeric_limits<T>::max() << std::endl;
    } else {
        std::cout << "  Min:      " << std::numeric_limits<T>::min() << std::endl;
        std::cout << "  Max:      " << std::numeric_limits<T>::max() << std::endl;
        std::cout << "  Epsilon:  " << std::numeric_limits<T>::epsilon() << std::endl;
        std::cout << "  Hassas:   " << std::numeric_limits<T>::digits10
                  << " basamak" << std::endl;
    }
    std::cout << std::endl;
}

int main() {
    printTypeInfo<int8_t>("int8_t");
    printTypeInfo<uint8_t>("uint8_t");
    printTypeInfo<int16_t>("int16_t");
    printTypeInfo<int32_t>("int32_t");
    printTypeInfo<int64_t>("int64_t");
    printTypeInfo<size_t>("size_t");
    printTypeInfo<float>("float");
    printTypeInfo<double>("double");

    // Sayı sistemleri gösterimi
    int32_t deger = 255;
    std::cout << "=== 255'in farkli gosterimleri ===" << std::endl;
    std::cout << "  Decimal:  " << std::dec << deger << std::endl;
    std::cout << "  Hex:      0x" << std::hex << deger << std::endl;
    std::cout << "  Octal:    0" << std::oct << deger << std::endl;
    std::cout << "  Binary:   " << std::bitset<8>(deger) << std::endl;
    std::cout << std::dec;  // Decimal'e geri dön

    return 0;
}

Özet

  • `int` boyutu platformdan platforma değişir — standart sadece minimum boyut belirtir. Bu yüzden boyut önemli olduğunda int32_t, int64_t gibi fixed-width türler kullan.

  • `<cstdint>` header'ı int8_t'den int64_t'ye ve bunların unsigned versiyonlarına kadar sabit genişlikli tam sayı türlerini sağlar. Dosya formatları, ağ protokolleri ve gömülü sistemlerde vazgeçilmezdir.

  • `size_t` boyut ve indeks değerleri için kullanılan unsigned bir türdür. Container'ların size() fonksiyonu ve sizeof operatörü size_t döndürür. Geriye sayarken dikkat — unsigned olduğu için sıfırın altına inemez.

  • Sayı sistemleri: decimal (varsayılan), binary (0b), octal (0), hexadecimal (0x). Başına sıfır koyulan sayılar octal olur — dikkat!

  • Literal suffix'ler (u, l, ll, f vb.) sayı sabitlerinin türünü açıkça belirler. Büyük sayılarla işlem yaparken taşmayı önlemek için LL suffix'i kullan.

  • `std::numeric_limits` her sayısal türün min, max ve diğer özelliklerini sorgular. Sihirli sayılar (magic numbers) yerine bunu kullanmak kodunu daha taşınabilir ve okunabilir yapar.