Why is there a pointer to a pointer (int ** a)?

int **a = malloc2d(M, N) // dynamically allocates a 2D array 

What is the purpose of having int ** a vice int * a. I understand that pointers are necessary for dynamic allocation, but why is there a pointer to a pointer?

For a 3-dimensional array, this will be:

 int ***a 

?

+6
c ++ pointers
source share
7 answers

You need to point to a pointer for several reasons.

In the example you specified, a double pointer is used to store the 2D array.

A 2D array in C is considered as a 1D array, whose elements are 1D arrays (strings).

For example, a 4x3 array from T (where "T" is a data type) can be declared: "T mat [4] [3]" and is described as follows:

  +-----+-----+-----+ mat == mat[0] ---> | a00 | a01 | a02 | +-----+-----+-----+ +-----+-----+-----+ mat[1] ---> | a10 | a11 | a12 | +-----+-----+-----+ +-----+-----+-----+ mat[2] ---> | a20 | a21 | a22 | +-----+-----+-----+ +-----+-----+-----+ mat[3] ---> | a30 | a31 | a32 | +-----+-----+-----+ 

Another situation is when you pass a pointer to a function and you want this function to highlight that pointer. To do this, you must specify the address of the pointer variable, therefore, a pointer to a pointer

 void make_foofoo(int** change) { *change = malloc(4); } 
+8
source share

In C, a pointer of type T indicates where some data of type T stored. To save specific things, I will talk about T = int below.

The simplest use of a pointer can be to point to a single value:

 int a = 42; int *pa = &a; 

Now *pa and a same and equal to 42 . In addition, *pa and pa[0] equivalent: for example, you can do:

 *pa += 1; /* a is 43 */ pa[0] += 1; /* a is 44 */ a += 1; /* a is 45 */ 

In fact, the C compiler automatically translates pa[0] to *(pa+0) .

A pointer may indicate a location within a data sequence:

 int arr[] = { 1, 2, 3 }; /* 3 ints */ int *parr = arr; /* points to 1 */ 

Now our memory looks like this:

  +---+---+---+ arr: | 1 | 2 | 3 | +---+---+---+ +------+ | | parr | ----+ +------+ 

parr is a pointer that points to the first arr element in the figure above. By the way, parr also has a field around it, because we need to store the parr object somewhere in memory. The parr value is the address of the first arr element.

Now we can use parr to access the arr elements:

 arr[0] == parr[0]; /* true */ parr[1]++; /* make arr[1] equal to 3 */ 

Thus, a pointer can be used to indicate: "I am pointing to the first of n elements in the continuous storage of some objects." Of course, you need to know how many objects for this circuit work: but as soon as we remember this, this is an extremely convenient way to access memory in C.

A pointer can also point to dynamically allocated memory:

 #include <stdlib.h> size_t n; /* now, obtain a value in n at runtime */ int *p = malloc(n * sizeof *p); 

If the call to malloc() succeeds higher, p now points to the first of the adjacent regions allocated for 10 int s. We can use p[0] through p[n-1] in our program.

You probably knew most or all of the above :-), but nevertheless, the above helps to understand what I will say next.

Remember, we said that a pointer can point to a continuous sequence of objects of the same type? The "same type" may be another type of pointer.

 #include <stdlib.h> int **pp; pp = malloc(3 * sizeof *pp); 

Now pp points to int * . Returning to the previous picture:

  +------+------+------+ | | | | +------+------+------+ +------+ | | pp | ----+ +------+ 

And each of the three fields is int * , which can point to the first element of the adjacent sequence int s:

 for (i=0; i < 3; ++i) pp[i] = malloc((i + 1) * sizeof *ppi[i]); 

Here we have allocated space for one int in pp[0] , 2 in pp[1] and 3 in pp[3] :

  +------+ +---+ pp -------->| |-------->| | +------+ +---+---+ | |-------->| | | +------+ +---+---+---+ | |-------->| | | | +------+ +---+---+---+ 

So, pp[0] is a pointer to a single int and that int is the only int in the dynamically allocated int s block. In other words, pp[0][0] is int , and points to the top of width-3 above. Similarly, pp[1][0] and pp[1][1] are valid and are two fields below the field pp[0][0] .

The most commonly used pointer to pointer is the creation of a two-dimensional "array" at runtime:

 int **data; size_t i; data = malloc(n * sizeof *data); for (i=0; i < n; ++i) data[i] = malloc(m * sizeof *data[i]); 

Now, assuming all malloc() succeed, data[0] ... data[n-1] are valid int * values, each of which points to a separate length m adjacent int objects.

But, as I showed above, a pointer to a pointer should not have the same "number of elements" in each of its "lines". The most obvious example is argv in main() .

Now, as you can guess, a 3-level deep pointer, such as int ***p; is ok and can be useful in C.

+7
source share

Just for fun, here is an example ***** :

  • We have a three-dimensional texture (volumetric texture) - 3 dimensions, therefore ***
  • each voxel in the texture has a color ( * )
  • texture animated, has a timeline ( * )

Therefore, we would have tex [2] [3] [10] [2] [10] .... that:

  • Coordinate 2,3,10 in 3D
  • color 2 (green)
  • frame 10

To transfer such data with the possibility of changing it, you need to pass int****** ... fun! Xd

(and therefore I like structure and classes ....)

+2
source share

Helps to draw a picture. Imagine a one-dimensional array in which each element contains a pointer. Each of these pointers points to a different one-dimensional array. malloc2d is not a standard library function, but I assume that it returns a two-dimensional array constructed in this way.

+1
source share

You can use this to create an array of size 2, like a math matrix or a chessboard. This way you can store a data table to a large extent.

+1
source share

A regular C array is a pointer to a block of memory.

A 2D array is a pointer to a list of pointers to each row of the array.

so, a pointer to a pointer.

+1
source share

you only ever missed the size of the pointer (most often 32 or 64 bits), and not whole objects.

0
source share

All Articles