Sıkı takas kuralı, işaretçi tür bilgisine dayalı agresif derleyici optimizasyonlarına olanak tanımak için C dilinin evriminden ortaya çıkmıştır. Standartlaştırmadan önce, farklı türdeki işaretçilerin farklı bellek alanlarına işaret ettiğini varsayamayan derleyiciler, bellekten olumsuz yeniden yükleme yapmak zorunda kalıyordu. C89 ve daha sonra C++98 standartları, uyumsuz bir tür üzerinden bir nesneye erişmenin belirsiz davranışı tetiklediğini resmileştirdi. Bu, derleyicilerin kayıtlar içinde değerleri saklamasına ve bellek işlemlerini güvenli bir şekilde yeniden sıralamasına olanak tanır.
Programcılar reinterpret_cast kullanarak bir int*'i bir float*'a dönüştürdüklerinde ve ardından derefere ettiklerinde, int ve float'ın farklı temsil biçimleri olan alakasız türler olduğundan dolayı sıkı takas kuralını ihlal ederler. Derleyici, bu işaretçilerin aynı belleğe karşılık gelmediğini varsaydığı için, talimatları sıraya koyabilir veya kayıt değerlerini yanlış bir şekilde önbelleğe alabilir. Bu, yalnızca yüksek optimizasyon seviyelerinde (-O2 veya -O3) ortaya çıkan ince hatalara yol açar ve sıklıkla eski veriler veya tamamen optimize olmuş kod yolları üretir.
C++20, alakasız türdeki aynı boyuttaki bir nesnenin bit düzeyinde kopyasını oluşturan constexpr dostu bir yardımcı program olan std::bit_cast'i tanıttı. reinterpret_cast'in aksine, std::bit_cast aliasing kurallarını ihlal etmez çünkü kavramsal olarak kaynak bitlerden yeni bir nesne oluşturur ve işaretçi aliasing'i gerektirmez. C++20'den önceki kod tabanları için, std::memcpy yasal bir alternatif olarak hizmet eder, ancak constexpr desteği yoktur ve açık bellek tamponları gerektirir.
32-bit ondalıklı değerlerin ağ sırası şeklinde CAN veriyolu üzerinden byte akışları olarak geldiği gömülü yazılımın sensör telemetrisini analiz etme. Sistem, SIL güvenlik sertifikası gereklilikleri için belirsiz davranış olmadan std::uint8_t tamponlarından float değerlerini yeniden oluşturmalıdır. Önceki uygulama işaretçi dönüştürmesi kullanmış ve MISRA uyumluluk kontrollerini geçememiştir ve yalnızca sürümderlemelerinde aralıklı hatalar sergilemiştir.
Byte tamponundan float*'a doğrudan reinterpret_cast. Bu yaklaşım sıfır yük ve doğrudan sözdizimi sunar. Ancak, float'ın uint8_t dizileriyle alias olamayacağı için sıkı takas ihlallerini tetikler ve derleyicinin ARM hedeflerinde bağlantı zamanında optimize edilmiş makine kodu oluşturmasına neden olur.
uint32_t ve float üyeleri ile bir union kullanarak tür punning. Derleyici uzantısı olarak yaygın olarak desteklense de, bu teknik C++ içinde teknik olarak belirsiz bir davranıştır, C içinde yasal olmasına rağmen. Aynı zamanda constexpr bağlamlarda kullanımını engeller ve -fstrict-aliasing uyarıları ile sıkı uyumluluk inşa edebilir.
Tampondan yerel bir float değişkenine std::memcpy. Bu yöntem iyi tanımlıdır ve modern derleyicilerde sıfır maliyetli montaja optimizasyon yapar. Dezavantajı, ayrıntılı sözdizimi ve constexpr fonksiyonlarında kullanım olanağının olmaması, sabit veri için çalışma zamanında başlatma gerektirmesidir.
std::bit_cast'in C++20'ye geçtikten sonra uygulanması. Bu, reinterpret_cast'in açıklığı ile sıkı standart uyumluluğu ve constexpr yeteneği sunar. Seçim, belirsiz davranışın yasak olduğu uzun vadeli sürdürülebilirlik ve güvenlik sertifikaları önceliği ile yapılmıştır.
Telemetri ayrıştırıcı, statik analiz ve MISRA C++ uyumluluk kontrollerini geçti. Birim testleri büyük ve küçük sonian sistemler boyunca bit düzeyinde doğruluğu teyit etti. Kod artık optimizasyonu ile -O3'te çalışırken başvuru gerektirmiyor.
Farklı türlere işaret eden işaretçiler aynı fiziksel bellek adresine işaret etse bile neden derleyici, bu işaretçilerin birbirine alias olmadığı varsayımını benimser?
Derleyicinin alias analizi, bellek bölgelerine farklı türler atanmasını sağlayan tür tabanlı alias analizi (TBAA) verilerine dayanır. TBAA, bir int yazımının bir float üzerindeki okunmayı etkilemeyeceğini kanıtlamak için optimizasyonu sağlar, böylece talimatların yeniden sıralanmasına ve kayıt tahsisine izin verir. Bu garanti olmadan, derleyici, performansı modern süperölçekli işlemcilerde ciddi şekilde azaltarak muhafazakar bellek engelleri ve yeniden yüklemeler yayımlamak zorunda kalır.
std::bit_cast ile constexpr ile uyumlu bir memcpy sarıcı arasındaki fark, montaj düzeyinde nedir?
Her ikisi de genellikle benzer taşıma talimatlarına derlense de, std::bit_cast standard tarafından constexpr olması garanti edilir ve hedef nesnenin önceden var olmasını gerektirmez. Bir constexpr memcpy sarıcısı, başlatılmamış bir depola yazmayı gerektirir ve sonucunu yasal olarak erişebilmek için muhtemel bir std::launder çağırmayı gerektirebilir. std::bit_cast, açık depolama yönetimi olmaksızın, hedef türünün bir prvalue'sini yaratmayı sağlar.
Sıkı alias ihlalleri statik analiz araçları veya sanitizörler tarafından tespit edilebilir mi ve neden belirgin ihlalleri yakalamakta başarısız olabilirler?
-fsanitize=undefined ile UBSan gibi araçlar, çalışma zamanında bazı alias ihlallerini tespit edebilir, ancak bunlar, önemli bir yük ekleyen enstrümantasyona dayanır ve optimizasyonun, kötüye kullanım olmayan varsayımına dayalı olarak kodu zaten dönüştürdüğü durumları kaçırabilir. Clang Statik Analizörü gibi statik analizörler, çeviri birimleri arasında alias analizinde çözümsüz sorunlarla karşı karşıyadır. Sonuç olarak, ihlaller genellikle optimize edilmiş sürümlerde sessiz bir derleme hatası olarak ortaya çıkar, bu da programcı bilgisini birincil savunma haline getirir.