C & C++ Dinamik Bellek Yönetimi & Extern C Bildirimi

11 minute read

Bu konuda fazla derine inmeden c dilinden c++ diline geçerken dinamik bellek yönetiminde bizi bekleyen bazı değişikliklerden bahsetmek istiyorum. Aynı zaman da extern c bildirimi ve interface tasarımı ile ilgili birkaç noktaya değinmek istiyorum.

  • C dilinde stdlib kütüphanesinde ki fonksiyonları kullanıyorduk.. (malloc, alloc vs.)
  • C++ dilinde bunlara gerek kalmayacak. Yeni operatörlerimiz var ismi new olan operatörler ve ismi delete olan operatörler.
  • new operatörler nesneyi HAYATA GETİRİR.
  • delete dinamik nesnelerin HAYATINI SONLANDIRIR.
  • malloc hayat başlatmıyor sadece yer ayırıyordu ama new nesneyi hayata getiriyor.(initialize etme, yazma) Bellekte bir nesnenin yerinin olması onu aslında nesne yapmıyor. Storage nesnenin gerek şartı ama yeter şartı değil.

NEDEN ?

  • Primitive türler olduğunda şu değişkene bir veri atarım diyebiliriz fakat sınıf olduğunda olay bu kadar basit olmayacak. O bellek alanın kullanılabilir bir hale getirilmesi için birçok şey yapılacak(kaynaklar edinilecek vs vs)
  • Yer elde etmek başka birşey, elde edilen yerde bir nesneyi hayata getirmek başka birşey.
  • Bellekte yeri olmayan nesneden bahsetmek mümkün değil.
  • new operatörünün 4–5 biçimi var, biz şimdilik 2 biçiminde bahsedeceğiz.
  • Primitive türlerden örnek: new int → int türden bir dinamik nesneyi hayata başlatıyorum
 1#include <iostream>
 2#include <stdlib.h>
 3
 4
 5int main() {
 6
 7    int *p1 = new int;
 8    int *p2 = new int[10];
 9
10    // kodlar
11
12    delete p1;
13    delete []p2;
14
15    return 0;
16
17}
18#include <iostream>
19#include <stdlib.h>
20
21int main() {
22
23    int *p1 = new int{};        // *p1 = 0
24    int *ptr1 = new int{12};      // *ptr1 = 12
25    int *ptr2 = new int(20);      // *ptr2 = 20
26
27    int *p2 = new int[10];
28
29    /// ....
30
31    delete p1;
32    delete []p2;
33
34    return 0;
35
36}
  • auto keyword’ü tanıttığı ismi, ilk değer vereceğimiz ifadenin türü yapar.
  • memory leak ile resource leak birbirine karıştırmamak gerek.
  • memory leak → bellek sızıntısı, resource → kaynak sızıntısı
  • İşimiz bittikten sonra nesneleri delete etmemiz gerekiyor. Etmezsek bellek sızıntısı olur, birçok user defined tür için de kaynak sızıntı işi olur.

Basitçe arka planda ne oluyor ?

  • new operatörü 2 işi birden yapıyor (derleyicinin oluşturduğu kodda)
  • Hayata gelen nesne için bellek alan gerekiyor. sizeof_t kadar. Arka planda bunu operator new isimli fonksiyona yaptırıyor
  • new operatörü ile operator new fonksiyonu (karıştırılmamalı)
  • operator fonksiyonları, isimlerinin doğal parçası operator… Örnek toplama operatörü ise operator+ gibi.
  • operator new, malloc un parametrik yapısının aynısı.

C++ niye malloc kullanılmamış?

  • Exception handling için
  • new operatörü bellek elde etmek dışında, sınıf nesnelerinin hayata gelmesi için gerekli olan constructor fonksiyonunu çağrıyor.
  • delete ise destructor’ı çağırır, operator delete kullanır.
  • C++ dilinde default olarak garbage collector olmaması sonucunda delete operatörü var. Biz de hazır bir garbage collector yok.
  • Dinamik nesne oluşturma ile, dinamik nesnelerden oluşan bir liste oluşturma sentaksı farklıdır.
 1operator delete(ptr); // free'nin karsiligi
 2
 3operator new   -> malloc
 4operator delete -> free
 5
 6operator new // Diger gorevi CONSTRUCTOR cagirmak
 7operator delete // Diger goreci DESTRUCTOR cagirmak
 8
 9// dinamik bir nesne hayata getirme
10T *p = new T;
11
12// exp kadar dinamik nesne olasaca
13new int[exp];
14
15
16// Cogul
17T *p = new T[n];
18delete []p;
19
20int *p1 = new int; // COP DEGER
21int *p2 = new int[10]; // COP DEGER
22
23
24// Tekil ise
25delete p1;
26
27// Cogul ise
28delete [p2]; // koseli parantez icine birsey yazilmaz
29
30int *p1 = new int{};    // *p1 = 0
31int *p1 = new int{12};  // *p1 = 12 ilk deger verilebilir
32
33new int(10);    // 1 tane
34new int[10];    // 10 tane
  • C++ 11 öncesi [] ile hayata getirilen primitive türlere ilk değer vermek yoktu. C++11 ile {} initializer list geldi.
1int n = 5;
2int *p = new int[n] {1, 2, 3, 4, 5};
  • Nesneleri kısmen öldürmek gibi birşey söz konusu değil. Toplu halde bildirim, toplu halde hayat bitirme.
 1// C Pointer, MALLOC
 2#include <stdio.h>
 3#include <stdlib.h>
 4#include <time.h>
 5
 6int main()
 7{
 8    int row, col;
 9    int **pd; // Elemanlari int* turunden olan bir dizinin adresi int** turundendir
10    int i, k;
11
12    srand((unsigned)time(NULL));
13
14    printf("Matrisiniz satir ve sutun sayisini giriniz: ");
15    scanf("%d%d", &row, &col);
16
17    pd = (int **) malloc(row * sizeof(int *));
18    if(pd == NULL){
19        printf("Cannot allocate memory!\n");
20        exit(EXIT_FAILURE);
21    }
22
23    for(i = 0; i < row; i++){
24        pd[i] = (int *) malloc(col * sizeof(int));
25        if(pd[i] == NULL){
26            printf("Cannot allocate memory!\n");
27            exit(EXIT_FAILURE);
28        }
29    }
30
31    for(i =0; i < row; i++)
32        for(k = 0; k < col; k++)
33            pd[i][k] = rand() % 10;
34
35
36    for(i =0; i < row; i++)
37        for(k = 0; k < col; k++)
38            printf("%d", pd[i][k]);
39        printf("\n");
40
41    for(i = 0; i < row; i++)
42        free(pd[i]);
43
44    free(pd);
45
46    return 0;
47}
48// C++
49#include <cstdlib>
50#include <ctime>
51#include <iostream>
52
53int main()
54{
55    int row, col;
56    srand(static_cast<unsigned>(time(nullptr)));
57
58    std::cout << "Matrisiniz satir ve sutun sayisini giriniz: ";
59    std::cin >> row >> col;
60    int **pd{new int *[row]};
61
62    for(int i = 0; i < row; i++)
63        pd[i] = new int[col];
64
65    for(int i =0; i < row; i++)
66        for(int k = 0; k < col; k++)
67            pd[i][k] = rand() % 10;
68
69
70    for(int i = 0; i < row; i++){
71        for(int k = 0; k < col; k++)
72            std::cout << pd[i][k];
73        std::cout << "\n";
74    }
75
76    for(int i = 0; i < row; i++)
77        delete []pd[i];
78
79    delete[]pd;
80
81    return 0;
82}
83

Not: endl alt satıra geçirmekten baska bufferı flush eder. http://www.cplusplus.com/reference/ostream/endl/


Extern C Bildirimi

  • C++ programcısı C++ kaynak dosyasında C dilinde derlenmiş bir dosyayı çağırıyor. Mesela C dilinde yazdığımız bir isprime fonksiyonunu derledim ve sonra C++ projesi oluşturuyorum. Compile time’da sorun yok ama runtime da problem oluyor. Bu linkera bağlı bir problem

  • C dilinde function overloading yok. C dilinde function overloading olmadığı için C de derleyiciler dışsal referans olarak bir fonksiyona atıfta bulunduklarında(bir fonksiyon inline değil ise derleyici object koda fonk çağırıldığına yönelik bir isim yazıyor özel bir notasyonla linker için yazıyor).

  • C derleyicileri bir fonksiyon çağrısını dışsal referansa dönüştürdüklerinde sadece fonksiyon isminden haraketle bir fonksiyon oluşturuyorlar.

  • C++ derleyicileri linkera overloadlardan hangisini çağırdığını anlamaları için parametrelerin türleri ve sayısını belirten bir notasyonda kullanmak zorunda

  • Bu problem, derleyiciye anlatmamız gerekiyor, bu bildirime extern c bildirimi deniyor. Bunun amacı bu fonksiyon C dilinde derlenmiştir, bu fonksiyona yapılan çağrıyı C nin notasyonuyla yaz, derleyici bulsun demek.

 1// kerem.h - Extern C Bildirimi
 2#ifdef __cplusplus
 3extern "C" { //extern c birkez yazip, blok icine almak
 4#endif
 5
 6    void k1(int);
 7    void k2(int);
 8    void k3(int);
 9    void k4(int);
10
11#ifdef __cplusplus
12}
13#endif

C++ Interface Tasarımı Hakkında

  • Client’ın ihtiyaç duymuyacağı bir ismi header dosyasına koymayın. Tasarım her zaman dıştan içe doğru yapılır.(interface tasarımı)
  • Önce başlık dosyası, sonra cpp (önce interface, sonra implemantasyon)
  • Tasarım dışardan içeriye olacak, interface’ten → implemantasyon’a