In actual programming, it often happens that the required memory space depends on the actual input data and cannot be predetermined. For such problems, it is difficult to solve using static arrays. To solve the above problems, the C language provides some memory management functions. These memory management functions, combined with pointers, can dynamically allocate memory space as needed to construct dynamic arrays, and can also reclaim unused space for later use, providing a means to effectively utilize memory resources.

A dynamic array is relative to a static array. The length of a static array is predefined, and once its size is given in the program, it cannot be changed. A dynamic array is different; it can be resized as the program requires. The memory space of a dynamic array is allocated from the heap (dynamic allocation). Storage space is allocated to it by executing code. It is only allocated when the program executes these statements. The programmer is responsible for freeing the memory.

Why use dynamic arrays?

In actual programming, it often happens that the required memory space depends on the actual input data and cannot be predetermined. For such problems, it is difficult to solve using static arrays. To solve the above problems, the C language provides some memory management functions. These memory management functions, combined with pointers, can dynamically allocate memory space as needed to construct dynamic arrays, and can also reclaim unused space for later use, providing a means to effectively utilize memory resources.

Comparison of dynamic arrays and static arrays

For static arrays, their creation is very convenient, they do not need to be freed after use, and referencing them is simple. However, their fatal weakness is that once created, their size cannot be changed!

For dynamic arrays, their creation is troublesome, and after use they must be freed by the programmer themselves; otherwise, it may seriously cause memory leaks. However, their use is very flexible, as they can dynamically allocate size according to program needs.

How to construct a dynamic array

Principles to follow

When allocating, allocate layer by layer from the outer layer to the inner layer.

When freeing, free layer by layer from the inner layer to the outer layer.

Pointers required for construction

To construct a one-dimensional dynamic array, a one-dimensional pointer is needed.

For two dimensions, one-dimensional and two-dimensional pointers are needed.

Three dimensions require one-, two-, and three-dimensional pointers.

And so on.

Functions required for construction

Function prototypeReturnFunction description
void *malloc(unsigned int size); Success: returns the starting address of the allocated space. Failure: returns a null pointer.Allocates heap space of size bytes from the system.
void *calloc(unsigned int num,  unsigned int size); Success: returns the starting address of the allocated space. Failure: returns a null pointer.Allocates heap space for num elements of size bytes by type.
void free(void *p); No return valueFrees the heap space pointed to by p.
void *realloc(void *p,unsigned int  size); Success: returns the starting address of the newly allocated space. Failure: returns a null pointer.Changes the heap space pointed to by p to size.

Explanation:

  • (1) It is specified as void * type. This does not mean that the function has no return value after the call, but rather that it returns the address of a node. The type of this address is void (no type or indeterminate type), that is, the starting address of a memory area. Its specific type cannot be determined until it is used based on the data of each field. It can be converted to other types using a cast. For example: double *pd=NULL; pd=(double *)calloc(10,sizeof(double));  This means applying to the system for 10 consecutive double-type storage spaces, and using pointer pd to point to the starting address of this consecutive space. Also, (double) is used to convert the return type of calloc() so that the address of double-type data can be assigned to pointer pd.

  • (2) The purpose of using sizeof is to calculate the number of bytes occupied by a type, in order to suit different compilers.

    (3) Since dynamic allocation may not always succeed, an exception handling routine should be added to prevent the program from stopping and leaving the user at a loss. Usually, such an exception handling segment is used: if(p==NULL) /* orif(!p)*/ { printf("Dynamic申pleaseMemoryfailure!\n"); exit(1); //ExceptionLogout }

  • (4) The header files for these four functions are all included inin.

  • (5) The allocated heap space has no name; it can only be found through the returned pointer.

  • (6) Never use free on a non-dynamically allocated storage block. Nor can you free the same memory area twice with free. For example: free(p);free(p);

  • (7) When calling free(), the memory pointed to by the passed pointer is released, but the pointer value in the calling function may remain unchanged, because p is passed to the function as a formal parameter. Strictly speaking, the value of the freed pointer is invalid, because it no longer points to the allocated memory area. Any use of it at this time may cause problems.

The difference between malloc and calloc

For the memory region allocated with malloc, if it has not been used before, every bit in it may be 0; conversely, if this part of memory has been previously allocated, freed, and reallocated, it may contain all kinds of leftover data. In other words, a program using malloc() may run normally at the beginning (when the memory space has not yet been reallocated), but after a period of time (after the memory space has been reallocated) problems may occur. Therefore, it must be initialized before use (memset can be used to initialize it to 0). However, the space allocated by calling calloc() is already initialized to 0 at the time of allocation. When choosing between calloc() and malloc(), you need to consider whether to initialize the allocated memory space, and then choose the corresponding function.

Specific construction method

Take the three-dimensional integer array array[n1][n2][n3] as an example.

First follow the principle of allocating layer by layer from the outer layer to the inner layer:

The outermost pointer is array, which is a three-dimensional pointer. What it points to is array[], which is a two-dimensional pointer. So for array

allocating memory should be:

array=(int***)calloc(n1,sizeof(int**));

The next layer pointer is array[], which is a two-dimensional pointer. What it points to is array[][], which is a one-dimensional pointer. So for array[]

allocating memory should be:

for(i=0;i<n1;i++)
{
    array[i]=(int**)calloc(n2,sizeof(int*));
}

The innermost pointer is array[][], which is a one-dimensional pointer. What it points to is array[][][], which is an integer constant. So to allocate memory for array[][], you should:

for(i=0;i<n1;i++)
{
    for(j=0;j<n2;j++)
    {
        array[i][j]=(int*)calloc(n3,sizeof(int));
    }
}

Of course, you can combine them together as:

int i,j,k;
int n1,n2,n3;
int ***array;
scanf("%d%d%d",&n1,&n2,&n3);
array=(int***)calloc(n1,sizeof(int**));
for(i=0;i<n1;i++)
{
    array[i]=(int**)calloc(n2,sizeof(int*));
    for(j=0;j<n2;j++)
    {
        array[i][j]=(int*)calloc(n3,sizeof(int));
        for(k=0;k<n3;k++)
        {
            array[i][j][k]=i+j+k+1;
        }
    }
}

Finally, don't forget to free this memory. This should follow the principle of freeing layer by layer from the inner layer to the outer layer.

For the analysis process, refer to the solution above; I won't go into details here. I'll just give the code:

for(i=0;i<n1;i++)
{
    for(j=0;j<n2;j++)
    {
        free(array[i][j]);//释放第三维指针
    }
}
for(i=0;i<n1;i++)
{
    free(array[i]);//释放第二维指针
}
free(array);//释放第一维指针

The creation process for other dimensions, such as four-dimensional, is much the same, so I won't go into details here.