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ür | Minimum Boyut | Tipik Boyut (x86-64) |
|---|---|---|
char | 1 byte | 1 byte |
short | 2 byte | 2 byte |
int | 2 byte | 4 byte |
long | 4 byte | 4 veya 8 byte |
long long | 8 byte | 8 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 sonekuö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_tveint64_tyeterli.int_fastveint_leastvaryantları 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çinintve boyut önemli olduğundaint32_t/int64_tkullan.
Ne Zaman Fixed-width Kullanmalı?
| Durum | Tercih |
|---|---|
| Genel amaçlı sayaç, döngü değişkeni | int 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ılar | int64_t |
| Dizin, boyut | size_t (birazdan göreceğiz) |
| Pixel, renk değeri | uint8_t (0-255 aralığı) |
| Bit manipülasyonu | uint32_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_tile geriye doğru sayarken dikkat et!size_tnegatif olamaz, bu yüzdeni >= 0koş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 = 10Binary, 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 = 63Octal, ç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 = 26Hexadecimal, 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 longOndalı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.00023Neden Ö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 * 1000gibi ifadelerde en az bir operandıLLsuffix'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?
| Özellik | sizeof | numeric_limits |
|---|---|---|
| Ne döndürür? | Byte cinsinden boyut | Min, max, özellikler |
| Kullanım | sizeof(int) → 4 | numeric_limits<int>::max() → 2147483647 |
| Derleme zamanı? | Evet | Evet (constexpr) |
| Tür kısıtı | Herhangi bir tür | Sayı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_tgibi fixed-width türler kullan.`<cstdint>` header'ı
int8_t'denint64_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 vesizeofoperatörüsize_tdö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,fvb.) sayı sabitlerinin türünü açıkça belirler. Büyük sayılarla işlem yaparken taşmayı önlemek içinLLsuffix'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.
AI Asistan
Sorularını yanıtlamaya hazır