3 Ağustos 2018 Cuma

Strict Aliasing - Belli Bir Tipteki Bellek Alanının Başka Bir Tipe Çevrilerek Kullanılmasının Yasak Olması

Giriş
Açıklaması şöyle.
strict aliasing is about accessing an object via a glvalue of the wrong type.
Açıklaması şöyle.
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
  • the dynamic type of the object,
  • a cv-qualified version of the dynamic type of the object,
  • a type similar (as defined in 4.4) to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • char or unsigned char type.
Strict aliasing belli bir tipteki bellek alanının başka bir tipe çevrilerek kullanılmasının yasak olması anlamına gelir.

Bu tür hataları yakalamak için derleyicinin tipine bağlı olarak şuna benzer birşey yaparız.
enable -fstrict-aliasing -Wstrict-aliasing=2
Pointer'ı Array' Dönüşüm
Pointer'ı array'e çeviremeyiz.
Örnek
Bu kod yanlış.
std::array<int, 8> a;
auto p = reinterpret_cast<int(*)[8]>(a.data());
(*p)[0] = 42;
Char Tipinden Daha Byük Bir Tipe Cast Ederek Erişim
Herhangi bir nesneye char pointer olarak erişebilirim.Eğer char tipindeki bellek alanını daha büyük bir tipin pointer'ına çevirip kullanırsam hata olur.
Genel Çözüm
Genel çözüm daha büyük tipe kopyalamak şeklinde. Şöyle yaparız.
uint16_t getMessageAt(const Message& msg, size_t i) {
  uint16_t tmp;
  memcpy(&tmp, msg.payload.data() + 2 * i, 2);
  return tmp;
}
Örnek
Elimizde şöyle bir kod olsun.
struct Message {
  int msg_type;
  std::vector<uint8_t> payload;
};
Şöyle yapamayız. uint8_t tipindeki bellek alanına erişmek için uint16_t pointer kullanılıyor.
const uint16_t* a = reinterpret_cast<uint16_t*>(msg.payload.data());
Örnek
Şöyle yapamayız. char tipindeki bellek alanına erişmek için float pointer kullanılıyor.


void a() {
  std::vector<char> v(sizeof(float));
  float *f = reinterpret_cast<float *>(v.data());
  *f = 42;
}

void b() {
  char *a = new char[sizeof(float)];
  float *f = reinterpret_cast<float *>(a);
  *f = 42;
}
Örnek
Şöyle yapamayız. Şu kod char tipindeki bellek alanına erişmek için short pointer kullanıyor.
short A[] = {1, 2, 3, 4, 5, 6};
std::cout << *(short*)((char*)A + 7) << std::endl;
Şöyle yaparız
short A[] = {1, 2, 3, 4, 5, 6};

short B;
std::copy(
  (char*)A + 7,
  (char*)A + 7 + sizeof(short),
  (char*)&B
);
std::cout << std::showbase << std::hex << B << std::endl;
Örnek
Elimizde değişken ve değişkenin bellek alanına erişen pointer'lar olsun.
constexpr uint64_t lil_endian = 0x65'6e'64'69'61'6e; 
    // a.k.a. Clockwise-Rotated Endian which allocates like
    // char[8] = { n,a,i,d,n,e,\0,\0 }

const auto& arr =   // std::array<char,8> &
    reinterpret_cast<const std::array<char,8> &> (lil_endian); //1

const auto& carr =  // char(&)[8]>
    reinterpret_cast<const char(&)[8]>           (lil_endian); //2

const auto* p =     // char *
    reinterpret_cast<const char *>(std::addressof(lil_endian)); //3
Bu pointer'ları kullanarak std::string nesneleri yaratalım.
const auto str1  = std::string(arr.crbegin()+2, arr.crend() );

const auto str2  = std::string(std::crbegin(carr)+2, std::crend(carr) );

const auto sv3r  = std::string_view(p, 8);
const auto str3  = std::string(sv3r.crbegin()+2, sv3r.crend() );
1 ve 2 Undefined Behavior'a sebep olur. Çünkü nesnenin bellek alanına char veya unsigned char olarak erişmedim.
float'tan int'e dönüşüm
Şöyle yapamayız. float 2 ile bölündükten sonra int gibi kullanılmaya çalışılıyor.
float sqrt(float f)
{
  const int result = 0x1fbb4000 + (*(int*)&f >> 1);
  return *(float*)&result;   
}

Hiç yorum yok:

Yorum Gönder