22 Kasım 2019 Cuma

Default Initialization - Değişkene Değer Atanmaz

Default Initialization Nedir?
Bir değişkene değer ileride yazılacağı farz edilerek, değer atanmadan yaratılması anlamına
gelir. Yani default initialization sonrasında değişkenin değeri belirsizdir.

Tamamen hızlı çalışmaya yönelik bir karardır. C++'ta hata yapılmasına çok müsait bir ortam oluşturmaktır. Bu yüzden Java gibi dillerde gerek yerel değişkenler (local variable) gerekse sınıfı değişkenleri (member variable) kesinlikle default initiliazation ile ilklendirilmezler!
Açıklaması şöyle.
Use of an uninitialized variable: This is commonly known as source of problems in C programs and there are many tools to catch these: from compiler warnings to static and dynamic analyzers. This improves performance by not requiring that all variables be zero initialized when they come into scope (as Java does). For most scalar variables, this would cause little overhead, but stack arrays and malloc'd memory would incur a memset of the storage, which could be quite costly, particularly since the storage is usually completely overwritten.
Örnek - Yerel Değişken
Default initializaiton kullanan basit bir örnek şöyle.
int i; //uninitialized
std::cin >> i;//read initial value
Örnek - Yerel Değişken
Derleyiciler "Path Analysis" yapıp yapmamak konusunda serbesttirler. İlklendirilmemiş değişkenler konusunda uyarı vermeyebilir Örnek'te iki tane ilklendirilmemiş değişken var ve derleyici uyarı vermiyor.
void Test ()
{
  int var1 ;      // not initialized at this point 
  int var2 ;      // not initialized at this point

  if (2 < var2)  
  {
    ...
  }
}
Default initialization kuralları şöyle.
- Eğer değişken bir array ise yine aynı şekilde garbage değer taşır.
- Eğer değişken bir sınıf ise ve default constructor'ı varsa çağırılır (bu durumda non-pod olur)
- Eğer sınıfın default constructor'ı yoksa derleyici bir tane yaratır ve onu çağırır.
İngilizcesi şöyle.
To default-initialize an object of type T means:
(7.1) — If T is a (possibly cv-qualified) class type (Clause 12), constructors are considered. The applicable constructors are enumerated (16.3.1.3), and the best one for the initializer () is chosen through overload resolution (16.3). The constructor thus selected is called, with an empty argument list, to initialize the object.
(7.2) — If T is an array type, each element is default-initialized.
(7.3) — Otherwise, no initialization is performed.

1. Stack'teki POD için Default Initialization
Alanlar ilklendirilmez.

Örnek
Elimizde şöyle bir POD olsun
class C { 
  int x;
};
Bu sınıfa derleyici constructor üretir ama x değerini ilklendirmez.
C c; // Compiler-provided default constructor is used
// Here `c.x` contains garbage
Örnek
Elimizde şöyle bir kod olsun
struct event_counts {
  std::uint64_t counts[MAX_COUNTERS];
  event_counts() = default;
};
Açıklaması şöyle.
Then default initialization
  event_counts counts;
will leave counts.counts uninitialized (default initialization is a no-op here), and value initialization
  event_counts counts{};
will value initialize counts.counts, effectively filling it with zeros.

2. Stack'teki non-POD için Default Initialization
Elle yazılan default constructor tüm alanlara değer atamazsa, alanlar default initialize edilir. Yani ilklendirilmez
MyNonPodClass instance1;//members will not be initialized
Örnek - Çok Bariz Undefined Behavior
Elimizde şöyle bir kod olsun. Bu kodda Foo yapısının x alanına bir değer atanıyor. Daha sonra Foo 
placement new ile ilklendiriliyor ve tüm belleği memset ile sıfırlanıyor
struct Foo {
  int x = 0;
  Foo() {}
};

// slightly simpler bar()
int bar(void* buf) {
  std::memset(buf, 0, sizeof(Foo));
  Foo* foo = new(buf) Foo;
  return foo->x; 
}
Üretilen assembly kodu şöyle
bar(void*):
        mov     DWORD PTR [rdi], 0   <----- memset(buff, 0, 4) or int x = 0? 
                                            They are redundant, so it's only done once.
        xor     eax, eax             <----- Set the return value to 0
        ret
Bu kodu şu hale getirelim
struct Foo {
  int x;
  Foo() {}
};
// ... same bar
Üretilen assembly kodu şöyle. Burada x alanına memset olmasına rağmen değer atanmadığı görülebilir.
bar(void*):
        mov     eax, DWORD PTR [rdi] <----- Just dereference buf as the result ?!?
        ret

3. Heap'teki POD için Default Initialization
new'den sonra () yapılırsa alanlar zero initalize edilir.
MyPodClass* instance3 = new MyPodClass;//members will not be initialized
MyPodClass* instance3 = new MyPodClass() ;//members are zero initialized

4. Global Değişkenler

Zero initialize edilir. Şöyle yaparız.
int i; //zero-initialized

struct A{
  int i;
};

struct B
{
  B(){};
  B(int i) :i{i}{}
  int i;
  int j;
};
A a; //a.i is zero-initialized

int main()
{
  int j;             //not initialized
  int k{};           //zero-initialized
  A b;               //b.i not initialized
  int* p = new int;  //*p not initialized
  A*   q = new A;    //q->i not initialized
  B ab;              //ab.i and ab.j not initialized
  B ab2{1};          //ab.j not initialized
  int xx[10];        //xx's element not initialized.

  int l = i;    //OK l==0;
  int m = j;    //undefined behavior (because j is not initialized)
  int n = b.i;  //undefined behavior 
  int o = *p; //undefined behavior 
  int w = q->i; //undefined behavior 
  int ex = x[0] //undefined behavior
}





Hiç yorum yok:

Yorum Gönder