简体   繁体   中英

How to allocate memory using a pointer to pointer in C

I am trying to understand code snippets in free a double pointer

and Why use double pointer? or Why use pointers to pointers?

I want to understand the difference between the following. Both snippets are from the above urls

int** pt; 

pt = (int*) malloc(sizeof(int)*10);

and

*pt = (int*) malloc(sizeof(int)*10);

Could you elaborate with some examples and drawing

First of all, the code snippet is bad for several reasons - the first is casting the result of malloc to the wrong type, and is using the wrong type to compute the amount of memory. Fixing the cast and type issues, we have:

int **pt;

 pt = malloc( sizeof  *pt * 10 );   // allocate space for 10 int *
*pt = malloc( sizeof **pt * 10 );   // allocate space for 10 int

After executing the first line, you have the following:

     int **                int *
    +---+                 +---+
pt: |   | --------------->|   | pt[0]
    +---+                 +---+       
                          |   | pt[1]
                          +---+
                          |   | pt[2]
                          +---+
                           ...
                          +---+
                          |   | pt[9]
                          +---+

You've set aside space for 10 int * objects, and pt points to the first of them.

The next line

*pt = malloc( sizeof **pt * 10 ); // allocate space for 10 int

allocates space for 10 int objects, and sets pt[0] to point to them:

     int **                int *                int 
    +---+                 +---+                +---+
pt: |   | --------------->|   | pt[0] -------->|   | pt[0][0]
    +---+                 +---+                +---+
                          |   | pt[1]          |   | pt[0][1]
                          +---+                +---+
                          |   | pt[2]          |   | pt[0][2]
                          +---+                +---+
                           ...                  ...
                          +---+                +---+
                          |   | pt[9]          |   | pt[0][9]
                          +---+                +---+

This illustrates one way of allocating a "jagged" array; you can still index it as pt[i][j] , but unlike a true 2D array the rows are not adjacent in memory, and each row may be a different length. You'd normally write that as

pt = malloc( sizeof *pt * ROWS );
if ( pt )
{
  for ( size_t r = 0; r < ROWS; r++ )
  {
    pt[r] = malloc( sizeof *pt[r] * COLS );
  }
}

When that's all done, you have something like this:

     int **           int *                 int
    +---+            +---+                 +---+---+     +---+
pt: |   | ---------> |   | pt[0] --------> |   |   | ... |   | pt[0][0] - pt[0][COLS-1]
    +---+            +---+                 +---+---+     +---+
                     |   | pt[1] ------+
                     +---+             |   +---+---+     +---+
                     |   | pt[2] ---+  +-> |   |   | ... |   | pt[1][0] - pt[1][COLS-1]
                     +---+          |      +---+---+     +---+
                      ...           | 
                                    |      +---+---+     +---+
                                    +----> |   |   | ... |   | pt[2][0] - pt[2][COLS-1]
                                           +---+---+     +---+

The following is wrong, compiler should complain about types:

int** pt;     
pt = (int*) malloc(sizeof(int)*10);

This is also wrong for another reason (here pt does not actually point to anything usable):

int** pt;     
*pt = (int*) malloc(sizeof(int)*10);

A pointer to T is a variable of type T * that may contain address of some memory that may contains elements of type T :

+------+
|      | pointer to T 
+------+
    |
    v
+-------------+-------------+-------------+
|             |             |             | elements of type T
+-------------+-------------+-------------+ 

For example, in C, to obtain what is drawn, you can write:

int *pi;
pi = malloc(sizeof(int)*3);

If you have a pointer to pointer to T then then diagram could be something like:

+------+
|      | pointer to pointer to T 
+------+
    |
    v
+------+------+------+
|      |      |      | pointers to T 
+------+------+------+
    |      |      |     +-------------+-------------+-------------+
    |      |      +---->|             |             |             | elements of type T
    |      |            +-------------+-------------+-------------+ 
    |      |     +-------------+-------------+
    |      +---->|             |             | elements of type T
    |            +-------------+-------------+ 
    |
    v
+-------------+-------------+-------------+-------------+
|             |             |             |             | elements of type T
+-------------+-------------+-------------+-------------+ 

and the code could be:

int **ppi;
ppi = malloc(sizeof(int *)*3);
ppi[0] = malloc(sizeof(int)*3);
ppi[1] = malloc(sizeof(int)*2);
ppi[2] = malloc(sizeof(int)*4);

Of course, malloc could fail and return value should be tested against failure.

Using the casting you helped the compiler to find an error in this code snippet

int** pt; 

pt = (int*) malloc(sizeof(int)*10);

For example the error message can look like

 error: assignment from incompatible pointer type [-Werror=incompatible-pointer-types]
  pt = (int*) malloc(sizeof(int)*10);
     ^

Without the casting the compiler could accept this evidently invalid code because the return type of the function malloc is void * and a pointer of the type void * may be assigned to a pointer to object of any other type.

That is in the right side of the assignment the evaluated expression has the type int * while in the left side of the assignment there is an object of the type int ** and there is no implicit conversion from the type int * to the type int ** .

This code snippet

int** pt; 

*pt = (int*) malloc(sizeof(int)*10);

is invalid by another reason. The pointer pt is not initialized by a valid address of an object. It has either indeterminate value if the pointer has automatic storage duration or NULL if the pointer has static storage duration. In any case its dereferencing results in undefined behavior.

So it would be correctly to write

int* pt; 
^^^^^^^
pt = (int*) malloc(sizeof(int)*10);

However this construction

int** pt; 

//...

*pt = (int*) malloc(sizeof(int)*10);

can be made valid in some context.

Let's assume that you declared a pointer

int *pt;

and want to initialize it in a function. In this case you have to pass the pointer to the function by reference. Otherwise the function will deal with a copy of the pointer and in this case the original pointer will not be assigned in the function.

So the corresponding code snippet can look as it is shown in the demonstrative program

#include <stdlib.h>
#include <stdio.h>

size_t f( int **pt )
{
    const size_t N = 10;

    *pt = (int*) malloc( sizeof( int ) * N );

    if ( *pt )
    {
        int value = 0;
        for ( size_t i = 0; i < N; i++ ) ( *pt )[i] = value++;
    }

    return *pt == NULL ? 0 : N;
}

int main( void )
{
    int *pt;

    size_t n = f( &pt );

    if ( n )
    {
        for ( size_t i = 0; i < n; i++ ) printf( "%d ", pt[i] );
        putchar( '\n' );
    }

    free( pt );
}

The program output is

0 1 2 3 4 5 6 7 8 9

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM