Detailed explanation of new/delete and malloc/free in C++


foreword

If you are interested in C++ learning, you can read this article: C/C++ Tutorial

1. new/delete

1. Preamble

In C++ programming, dynamic memory allocation is a very common operation. new and delete are dynamic memory allocation operators provided in C++, they can be used to dynamically allocate any type of memory, and do not need to explicitly specify the size of the memory block.

2. How to use

2.1. Basic syntax of new and delete

new and delete are keywords in C++ for dynamically allocating and freeing memory. The basic syntax for new and delete is as follows:

// 动态分配一个对象
Type* p = new Type;

// 释放已经分配的对象
delete p;

Among them, Typerepresents the data type to be dynamically allocated, pand is a pointer to the data type. When new Typethe operation , the system will allocate a block of memory for this type of object and return a pointer to this memory block. When the object is no longer needed, the memory can be released through deletethe operation .

If you need to allocate multiple objects at the same time, you can use the following syntax:

// 动态分配一个数组
Type* p = new Type[n];

// 释放已经分配的数组
delete [] p;

Among them, nrepresents the number of objects to be allocated, pand is a pointer to an array of Typetype .

It should be noted that when using newfor dynamic memory allocation, if there is insufficient memory, std::bad_allocan exception will be thrown. Therefore, this exception should be handled in the program.

2.2. The underlying implementation principle of new and delete

New and delete also have some details to pay attention to in the underlying implementation. When performing new Typethe operation , the following steps are actually carried out in sequence:

  1. Call the operator new function to apply for a piece of memory with a size sizeof(Type)of .
  2. Call the constructor of the class to initialize the requested memory space.
  3. Returns a pointer to the requested memory space.

When performing delete pthe operation , the following steps are actually carried out in sequence:

  1. Call the destructor of the class to release the resources occupied by the object.
  2. Call the operator delete function to release the memory occupied by the object.

Among them, operator new and operator delete are keywords provided in C++. The operator new function is responsible for applying for memory, while the operator delete function is responsible for releasing memory.

It should be noted that, unlike malloc/free, new/delete can call the constructor and destructor of the class, and automatically calculate the required memory space size. This is also a big advantage of using new/delete.

3. The underlying principle

3.1. operator new 和 operator delete

The operator new function and operator delete function in C++ are used to dynamically allocate and release memory. The operator new function is responsible for applying for memory, while the operator delete function is responsible for releasing memory.

Here is one implementation of operator new:

void* operator new(size_t size) {
    
    
    if (size == 0) {
    
    
        size = 1;
    }
    void* ptr = malloc(size);
    if (!ptr) {
    
    
        throw std::bad_alloc();
    }
    return ptr;
}

Among them, sizeindicates the size of the memory space to be applied. If the value is 0, set it to 1. Then call the malloc function to apply for a specified size of memory space, if the application fails, std::bad_allocan exception .

The following is an implementation of operator delete:

void operator delete(void ptr) noexcept {
    
    
    free(ptr); 
}

Where ptris the memory space pointer to be freed. noexceptThe keyword is used here to indicate that the function will not throw any exceptions.

Note that when using operator new and operator delete functions, you need to call the constructor and destructor of the class yourself.

3.2. The underlying implementation principles of new and delete

The new and delete keywords actually encapsulate and overload the operator new and operator delete functions in the underlying implementation, for the convenience of programmers. Here's one implementation of new and delete:

void* operator new(size_t size) {
    
    
    if (size == 0) {
    
    
        size = 1;
    }
    void* ptr = malloc(size);
    if (!ptr) {
    
    
        throw std::bad_alloc();
    }
    return ptr;
}

void operator delete(void* ptr) noexcept {
    
    
    free(ptr);
}

void* operator new[](size_t size) {
    
    
    if (size == 0) {
    
    
        size = 1;
    }
    void* ptr = malloc(size);
    if (!ptr) {
    
    
        throw std::bad_alloc();
    }
    return ptr;
}

void operator delete[](void* ptr) noexcept {
    
    
    free(ptr);
}

As you can see, new and delete actually overload operator new and operator delete.

Note that for complex data structures, when using new[] to apply for multiple memories, an additional piece of 4-byte memory will be applied for storing the number of current applications. Use delete[] to know how many times the destructor of the object is called. But this data is not visible to the outside world

4. Precautions

When using new/delete for dynamic memory allocation, you need to pay attention to the following points:

  • Memory leaks: Memory that is no longer used must be released in time, otherwise memory leaks may result.
  • Dangling pointer: The memory block pointer that has been freed can no longer be accessed, otherwise it may cause the program to crash or other unpredictable errors.
  • Do not release memory repeatedly: the same memory block can only be released once, otherwise it may cause program crashes or other unpredictable errors.
  • Multi-threaded environment: When multiple threads access the same block of memory at the same time, it is necessary to adopt an appropriate synchronization mechanism to ensure thread safety.

5. Summary

In C++ programming, the appropriate dynamic memory allocation method should be selected according to the specific situation. When using dynamic memory allocation, good programming habits should be followed to ensure the correctness and stability of the program. At the same time, it is also necessary to avoid problems such as memory leaks, dangling pointers, and repeated release of memory to ensure the robustness and stability of the program.

Two, malloc/free

1. Preamble

malloc and free are dynamic memory allocation functions provided in C, they can be used to dynamically allocate any type of memory, and do not need to explicitly specify the size of the memory block

2. How to use

2.1. The basic syntax of malloc and free

malloc and free are functions in C that are used to dynamically allocate and free memory. The following is the basic syntax of malloc and free:

// 动态分配一块内存
Type* p = (Type*)malloc(sizeof(Type));

// 释放已经分配的内存
free(p);

Among them, Typerepresents the data type to be dynamically allocated, pand is a pointer to the data type. When malloc(sizeof(Type))the operation , the system will allocate a block of memory for this type of object and return a pointer to this memory block. When the object is no longer needed, the memory can be released through freethe operation .

It should be noted that when using mallocfor dynamic memory allocation, if insufficient memory occurs, a null pointer will be returned. Therefore, this situation should be handled in the program.

2.2. The underlying implementation principle of malloc and free

Malloc and free also have some details to pay attention to in the underlying implementation. When performing malloc(sizeof(Type))the operation , the following steps are actually carried out in sequence:

  1. Call the system function sbrk to expand the data segment of the program. (windows will call win API to achieve this function)
  2. Link the requested memory block with the used memory block.
  3. Returns a pointer to the requested memory space.

When performing free(p)the operation , the following steps are actually carried out in sequence:

  1. Marks the block of memory pointed to by p as unused.
  2. Merges the memory block pointed to by p with other unused memory blocks.
  3. If the merged memory block is not occupied, the memory block is released.

It should be noted that when using malloc/free for dynamic memory allocation, you need to call the constructor and destructor of the class yourself, and you cannot calculate the required memory space.

3. The underlying principle

3.1. sbrk function

The sbrk function is a system call used to expand the program's data segment. In Linux systems, the sbrk function can return the current heap top address, and can move the heap top address up or down by the specified number of bytes.

The following is a simple example that demonstrates how to use the sbrk function to obtain the current heap top address:

#include <unistd.h>
#include <iostream>

int main() {
    
    
    void* p1 = sbrk(0);  // 获取当前堆顶部地址
    std::cout << "p1 = " << p1 << std::endl;

    void* p2 = sbrk(1024);  // 将堆顶部地址向上移动 1024 字节
    std::cout << "p2 = " << p2 << std::endl;

    void* p3 = sbrk(-512);  // 将堆顶部地址向下移动 512 字节
    std::cout << "p3 = " << p3 << std::endl;

    void* p4 = sbrk(0);  // 再次获取当前堆顶部地址
    std::cout << "p4 = " << p4 << std::endl;

    return 0;
}

The window system also has its own win API that can be used to allocate heap memory

3.2. Memory block management

malloc and free also need to manage memory blocks in the underlying implementation. When using malloc for dynamic memory allocation, the system will add some additional information to the requested memory block

For example, the size of the memory block, a pointer to the next memory block, etc. These information will be saved at the beginning of the memory block and will not affect the user program's access to the memory space.

The following is a simple example that demonstrates how to obtain a block of memory through the malloc function, and the information contained in the memory block:

#include <iostream>
#include <cstdlib>

int main() {
    
    
    int* p = (int*)malloc(sizeof(int) * 10);  // 动态分配一块内存,可以存放 10 个 int 类型的变量
    if (!p) {
    
    
        std::cout << "Memory allocation failed" << std::endl;
        return -1;
    }

    std::cout << "Allocate memory at address " << p << std::endl;
    std::cout << "The size of the memory block is " << sizeof(int) * 10 << " bytes" << std::endl;

    int* next_p = (int*)(((char*)p) + sizeof(int) * 10);
    std::cout << "The pointer to the next memory block is " << next_p << std::endl;

    free(p);  // 释放内存

    return 0;
}

3.3. Memory Alignment

When using malloc for dynamic memory allocation, you need to consider the issue of memory alignment.

The so-called memory alignment means that the address where the data type is placed in the memory must meet certain conditions.

Specifically, each data type has an alignment associated with it, which is usually equal to the size of the data type (for example, an int has an alignment of 4 bytes). When allocating memory, the system will ensure that the starting address of the requested memory block is an integer multiple of the alignment value.

The following is a simple example that demonstrates how to obtain a block of memory through the malloc function and demonstrates the effect of memory alignment:

#include <iostream>
#include <cstdlib>

struct MyStruct {
    
    
    double x;
    char c;
    int i;
};

int main() {
    
    
    std::cout << "Size of MyStruct is " << sizeof(MyStruct) << " bytes" << std::endl;

    MyStruct* p1 = (MyStruct*)malloc(sizeof(MyStruct));
    if (!p1) {
    
    
        std::cout << "Memory allocation failed" << std::endl;
        return -1;
    }

    std::cout << "Allocate memory at address " << p1 << std::endl;

    char* p2 = (char*)p1;
    for (int i = 0; i < sizeof(MyStruct); ++i) {
    
    
        std::cout << (int)p2[i] << " ";
    }
    std::cout << std::endl;

    free(p1);  // 释放内存

    return 0;
}

It can be seen that when allocating memory of type MyStruct, the system will ensure that the returned starting address is an integer multiple of the size of type double.

4. Precautions

When using malloc/free for dynamic memory allocation, you need to pay attention to the following points:

  • Memory leaks: Memory that is no longer used must be released in time, otherwise memory leaks may result.
  • Dangling pointer: The memory block pointer that has been freed can no longer be accessed, otherwise it may cause the program to crash or other unpredictable errors.
  • Do not release memory repeatedly: the same memory block can only be released once, otherwise it may cause the program to crash or

Other unpredictable errors.

  • Memory out-of-bounds: Accessing a freed memory block, or accessing an address beyond the range of the allocated memory block, may cause the program to crash or other unpredictable errors.
  • Do not mix malloc/free and new/delete: One memory allocation method should be uniformly used in the same program, do not mix malloc/free and new/delete, otherwise it may cause memory management problems.

5. The difference between new/delete and malloc/free

new and delete are dynamic memory allocation operators provided in C++, they are functionally similar to malloc/free.

The use of new/delete is simpler and more intuitive than malloc/free. In addition, new/delete has the following advantages:

  • Type safety: new/delete can automatically calculate the required memory space size according to the type, without manual specification.
  • Automatically call the constructor and destructor: new can automatically call the constructor of the class, and delete can automatically call the destructor of the class, which makes it easier to manage the life cycle of the object.
  • Support overloading: new/delete operators can be overloaded, so that some special functions can be realized.

It should be noted that when using new/delete for dynamic memory allocation, problems such as memory leaks, dangling pointers, and memory out-of-bounds may also occur. Therefore, when writing a program, the problem of dynamic memory allocation and release must be carefully handled to avoid the above problems.

6. Summary

In the process of using dynamic memory allocation, you need to pay attention to problems such as memory leaks, dangling pointers, and memory out-of-bounds, and you also need to choose an appropriate memory allocation method according to the specific situation.

Guess you like

Origin blog.csdn.net/weixin_50964512/article/details/130075826