C++ Dynamic Memory Allocation: Basic Usage of new and delete

In C++, we often need to handle data storage issues. Sometimes, we may not know how much memory is required, or need to dynamically adjust the memory size based on runtime conditions. In such cases, static memory allocation (such as directly defining variables) is insufficient, and dynamic memory allocation becomes necessary.

Why Dynamic Memory Allocation?

In our daily coding, static memory allocation (e.g., int a; or char s[20];) determines the memory size at compile time and is automatically managed by the compiler. However, static memory lacks flexibility for scenarios like storing an uncertain number of data points (e.g., multiple numbers input by the user) or temporarily requiring a large block of memory.

Dynamic memory allocation allows us to request memory from the “heap” at runtime based on actual needs and manually release it after use. The heap is a flexible, independent memory area, but it requires careful management to avoid memory leaks.

Simple Differences Between Dynamic Memory, Heap, and Stack

  • Stack: Automatically allocated and freed by the compiler, used for local variables, function parameters, etc. It has limited size and is fast.
  • Heap: A dynamically requested memory area during program execution, with flexible size but manual management by the programmer (requires explicit release).

new and delete are the core tools for operating on heap memory.

new Operator: Allocating Memory on the Heap

To allocate memory on the heap, we use the new operator. Its syntax is straightforward:

1. Allocating a Single Object

Type* pointer = new Type;  // Allocates memory for one object of type Type

Example: Allocate a dynamic int variable:

int* p = new int;  // p points to a heap-allocated int

We can access or modify the memory via the pointer:

*p = 100;  // Store 100 in the memory pointed to by p
cout << "Value of dynamic variable: " << *p << endl;  // Output: 100

To initialize the memory during allocation, add initial values in parentheses after new:

int* num = new int(20);  // Directly initialize to 20
cout << "Initialized variable: " << *num << endl;  // Output: 20

2. Allocating an Array

For a collection of data (e.g., an array), use new[]:

Type* arrayPointer = new Type[arraySize];  // Allocates memory for an array of Type

Example: Allocate a dynamic array of 5 ints:

int* arr = new int[5];  // arr points to an array of 5 ints

Array elements are uninitialized by default (values may be random). To initialize them (e.g., to 0):

int* arr = new int[5]();  // All elements initialized to 0

Access elements via subscripts:

for (int i = 0; i < 5; ++i) {
    arr[i] = i * 2;  // Assign values: 0, 2, 4, 6, 8
}
for (int i = 0; i < 5; ++i) {
    cout << "arr[" << i << "] = " << arr[i] << endl;
}

delete Operator: Freeing Heap Memory

Memory allocated with new is not automatically freed. It must be manually released using delete or delete[] to avoid memory leaks (memory that is never released, causing the program to crash eventually).

1. Freeing a Single Object

Use delete to release memory for a single object:

int* p = new int(100);
// Use p...
delete p;  // Frees the memory pointed to by p; *p is now invalid

2. Freeing an Array

Use delete[] to release memory for an array (critical: delete alone will only free the first element):

int* arr = new int[5];
// Use arr...
delete[] arr;  // Must use delete[] to free the entire array

Common Errors and Precautions

  1. Forgetting to Free Memory: Failing to delete after new causes memory leaks:
   int* p = new int(5);
   // Use p...
   // Missing: delete p;  // Memory remains occupied
  1. Using delete for Arrays: Using delete instead of delete[] for arrays only frees the first element:
   int* arr = new int[10];
   delete arr;  // Error! Should use delete[] arr;
  1. Double Freeing Memory: A pointer cannot be deleted twice (undefined behavior):
   int* p = new int;
   delete p;
   delete p;  // Error! Double free causes program crash

Summary

Dynamic memory allocation enables flexible memory management at runtime, with new and delete as core tools:
- new allocates single objects (new Type) or arrays (new Type[size]).
- delete frees single objects, and delete[] frees arrays.
- Always match allocation and deallocation: use delete for single objects and delete[] for arrays to avoid leaks or crashes.

Proper use of dynamic memory improves efficiency, but strict attention to allocation and release is critical. Adopting good habits ensures robust memory management.

Xiaoye