Verwendung von malloc vs new-Allokatoren in C++

Jinku Hu 12 Oktober 2023
  1. Verwenden Sie den new-Operator, um dynamischen Speicher in C++ zu allozieren
  2. Mit dem new-Operator und std::unique_ptr dynamischen Speicher in C++ allozieren
  3. Verwenden Sie die Funktion malloc und realloc/reallocarray, um dynamischen Speicher zu allozieren
Verwendung von malloc vs new-Allokatoren in C++

In diesem Artikel werden verschiedene Methoden der Verwendung von malloc vs. new-Allokatoren in C++ erläutert.

Verwenden Sie den new-Operator, um dynamischen Speicher in C++ zu allozieren

new ist die bevorzugte Schnittstelle, um dynamischen Speicher in C++ direkt zu verwalten. Sie konstruiert ein Objekt des angegebenen Typs und gibt den Zeiger auf dieses Objekt zurück. Objekte, die mit dem new-Operator alloziert werden, sind standardmäßig initialisiert, was bedeutet, dass Objekte vom eingebauten und zusammengesetzten Typ Garbage-Werte haben, die vor der Verwendung initialisiert werden müssen.

Beachten Sie, dass new mit verschiedenen Notationen aufgerufen werden kann, um verschiedenen Bedürfnissen gerecht zu werden, aber im folgenden Beispiel weisen wir das Array int der Größe 10 zu. Daher zeigt der zurückgegebene Zeiger, der in der Variablen arr1 gespeichert ist, auf den Speicherabschnitt, der 40 Bytes groß ist. Die Funktion initPrintIntVector ist nur implementiert, um ein praktisches Kodierbeispiel besser zu demonstrieren. Da wir einen sogenannten nackten Zeiger verwenden, ist es wichtig, den allozierten Speicher mit dem delete-Operator freizugeben, bevor das Programm beendet wird. Beachten Sie aber, dass Klammern nach dem delete auch notwendig sind, um jede Stelle im Array wieder freizugeben.

#include <iomanip>
#include <iostream>
#include <random>
#include <vector>

using std::cout;
using std::endl;
using std::setw;
using std::vector;

constexpr int SIZE = 10;
constexpr int NEW_SIZE = 20;
constexpr int MIN = 1;
constexpr int MAX = 1000;

void initPrintIntVector(int *arr, const int &size) {
  std::random_device rd;
  std::default_random_engine eng(rd());
  std::uniform_int_distribution<int> distr(MIN, MAX);

  for (int i = 0; i < size; ++i) {
    arr[i] = distr(eng) % 1000;
    cout << setw(2) << arr[i] << "; ";
  }
  cout << endl;
}

int main() {
  int *arr1 = new int[SIZE];

  initPrintIntVector(arr1, SIZE);

  delete[] arr1;
  return EXIT_SUCCESS;
}

Ausgabe (*zufällig):

 8; 380; 519; 536; 408; 666; 382; 244; 448; 165;

Mit dem new-Operator und std::unique_ptr dynamischen Speicher in C++ allozieren

Auch wenn der new-Operator ein gutes Werkzeug für die dynamische Speicherallokation zu sein scheint, kann er in großen Codebases mit intensiven Speichermanipulationen recht fehleranfällig werden. Die Deallokation der Speicherressourcen zum richtigen Zeitpunkt ist nämlich ein ziemlich schwer zu bewältigendes Problem, das meist zu Speicherlecks oder unerwarteten Laufzeitfehlern führt. Aus diesem Grund hat die Standardbibliothek seit der C++ 11-Version intelligente Zeiger hinzugefügt, die den Speicher, auf den sie zeigen, automatisch löschen. std::unique_ptr ist ein Typ von intelligenten Zeigern, der nur sich selbst erlaubt, auf das gegebene Objekt zu zeigen. Beachten Sie, dass die Zuweisung immer noch mit dem new-Operator erfolgt, und nachdem der Zeiger verwendet wurde, können wir das Programm ohne Aufruf von delete verlassen.

#include <iomanip>
#include <iostream>
#include <random>
#include <vector>

using std::cout;
using std::endl;
using std::setw;
using std::vector;

constexpr int SIZE = 10;
constexpr int NEW_SIZE = 20;
constexpr int MIN = 1;
constexpr int MAX = 1000;

void initPrintIntVector(int *arr, const int &size) {
  std::random_device rd;
  std::default_random_engine eng(rd());
  std::uniform_int_distribution<int> distr(MIN, MAX);

  for (int i = 0; i < size; ++i) {
    arr[i] = distr(eng) % 1000;
    cout << setw(2) << arr[i] << "; ";
  }
  cout << endl;
}

int main() {
  std::unique_ptr<int[]> arr2(new int[SIZE]);

  initPrintIntVector(arr2.get(), SIZE);

  return EXIT_SUCCESS;
}

Ausgabe:

985; 885; 622; 899; 616; 882; 117; 121; 354; 918;

Verwenden Sie die Funktion malloc und realloc/reallocarray, um dynamischen Speicher zu allozieren

Auf der anderen Seite kann der C++-Code die ursprüngliche C-artige Allokationsfunktion - malloc - aufrufen, die für moderne C++-Standards eine ziemlich archaische Art der dynamischen Speichermanipulation ist. Es ist nicht die empfohlene Art, Objekte auf dem Heap zu allozieren, aber auf der Plusseite bietet malloc eine flexiblere Funktionalität.

malloc wird mit einem einzigen Argument aufgerufen, das das sizeof-Objekt angibt, und es gibt ein void* zurück, das in C++ auf den entsprechenden Typ gecastet werden sollte. Der einzige Vorteil des mit malloc allozierten Speichers ist, dass er mit der Funktion realloc oder reallocarray erweitert/geschrumpft werden kann. Die Funktion realloc nimmt den Zeiger auf das Objekt und die neue Größe als Argumente, während reallocarray den Zeiger, die Anzahl der Elemente und die Größe der einzelnen Elemente nimmt. Beachten Sie, dass, wenn der Objektspeicher erweitert wird, die alten gespeicherten Werte erhalten bleiben und neu hinzugefügte Elemente nicht initialisiert werden. Daher gibt das folgende Beispiel die erweiterten arr3-Elemente nur zu Demonstrationszwecken aus, was im realen Programm nicht der Fall sein sollte.

#include <iomanip>
#include <iostream>
#include <random>
#include <vector>

using std::cout;
using std::endl;
using std::setw;
using std::vector;

constexpr int SIZE = 10;
constexpr int NEW_SIZE = 20;
constexpr int MIN = 1;
constexpr int MAX = 1000;

void initPrintIntVector(int *arr, const int &size) {
  std::random_device rd;
  std::default_random_engine eng(rd());
  std::uniform_int_distribution<int> distr(MIN, MAX);

  for (int i = 0; i < size; ++i) {
    arr[i] = distr(eng) % 1000;
    cout << setw(2) << arr[i] << "; ";
  }
  cout << endl;
}

void printIntVector(int *arr, const int &size) {
  for (int i = 0; i < size; ++i) {
    cout << setw(2) << arr[i] << "; ";
  }
  cout << endl;
}

int main() {
  int *arr3 = static_cast<int *>(malloc(SIZE * sizeof(int)));
  //    int *arr3 = static_cast<int *>(malloc(SIZE * sizeof *arr3));
  //    int *arr3 = static_cast<int *>(malloc(sizeof(int[SIZE])));

  initPrintIntVector(arr3, SIZE);

  arr3 = static_cast<int *>(reallocarray(arr3, NEW_SIZE, sizeof(int)));
  // arr3 = static_cast<int *>(realloc(arr3, NEW_SIZE * sizeof(int)));
  printIntVector(arr3, NEW_SIZE);

  free(arr3);
  return EXIT_SUCCESS;
}

Ausgabe:

128; 346; 823; 134; 523; 487; 370; 584; 730; 268;
128; 346; 823; 134; 523; 487; 370; 584; 730; 268;  0;  0;  0;  0;  0;  0;  0;  0;  0;  0;
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Verwandter Artikel - C++ Memory