简体   繁体   中英

How many pointers are in an array of pointers

I dynamically allocated memory for 3D array of pointers. My question is how many pointers do I have? I mean, do I have X·Y number of pointers pointing to an array of double or X·Y·Z pointers pointing to a double element or is there another variant?

double*** arr;
arr = (double***)calloc(X, sizeof(double));
for (int i = 0; i < X; ++i) {
    *(arr + i) = (double**)calloc(Y, sizeof(double));
        for (int k = 0; k < Y; ++k) {
            *(*(arr+i) + k) = (double*)calloc(Z, sizeof(double));
        }
    }

The code you apparently intended to write would start:

double ***arr = calloc(X, sizeof *arr);

Notes:

  • Here we define one pointer, arr , and set it to point to memory provided by calloc .
  • Using sizeof (double) with this is wrong; arr is going to point to things of type double ** , so we want the size of that. The sizeof operator accepts either types in parentheses or objects. So we can write sizeof *arr to mean “the size of a thing that arr will point to”. This always gets the right size for whatever arr points to; we never have to figure out the type.
  • There is no need to use calloc if we are going to assign values to all of the elements. We can use just double ***arr = malloc(X * sizeof *arr); .
  • In C, there is no need to cast the return value of calloc or malloc . Its type is void * , and the compiler will automatically convert that to whatever pointer type we assign it to. If the compiler complains, you are probably using a C++ compiler, not a C compiler, and the rules are different.
  • You should check the return value from calloc or malloc in case not enough memory was available. For brevity, I omit showing the code for that.

Then the code would continue:

for (ptrdiff_t i = 0; i < X; ++i)
{
    arr[i] = calloc(Y, sizeof *arr[i]);
    …
}

Notes:

  • Here we assign values to the X pointers that arr points to.
  • ptrdiff_t is defined in stddef.h . You should generally use it for array indices, unless there is a reason to use another type.
  • arr[i] is equivalent to *(arr + i) but is generally easier for humans to read and think about.
  • As before sizeof *arr[i] automatically gives us the right size for the pointer we are setting, arr[i] . Finally, the in there is:
        for (ptrdiff_t k = 0; k < Y; ++k)
            arr[i][k] = calloc(Z, sizeof *arr[i][k]);

Notes:

  • Here we assign values to the Y pointers that arr[i] points to, and this loop is inside the loop on i that executes X times, so this code assigns XY pointers in total.

So the answer to your question is we have 1 + X + XY pointers.

Nobody producing good commercial code uses this. Using pointers-to-pointers-to-pointers is bad for the hardware (meaning inefficient in performance) because the processor generally cannot predict where a pointer points to until it fetches it. Accessing some member of your array, arr[i][j][k] , requires loading three pointers from memory.

In most C implementations, you can simply allocate a three-dimensional array:

double (*arr)[Y][Z] = calloc(X, sizeof *arr);

With this, when you access arr[i][j][k] , the compiler will calculate the address (as, in effect, arr + (i*Y + j)*Z + k ). Although that involves several multiplications and additions, they are fairly simple for modern processors and are likely as fast or faster than fetching pointers from memory and they leave the processor's load-store unit free to fetch the actual array data. Also, when you are using the same i and/or j repeatedly, the compiler likely generates code that keeps i*Y and/or (i*Y + j)*Z around for multiple uses without recalculating them.

Well, short answer is: it is not known .

As a classic example, keep in mind the main() prototype

    int    main( int argc, char** argv);

argc keeps the number of pointers. Without it we do not know how many they are. The system builds the array argv , gently updates argc with the value and then launches the program.

Back to your array

    double*** arr;

All you know is that

  • arr is a pointer.
  • *arr is double** , also a pointer
  • **arr is double* , also a pointer
  • ***arr is a double .

What you will get in code depends on how you build this. A common way if you need an array of arrays and things like that is to mimic the system and use a few unsigned and wrap them all with the pointers into a struct like

typedef struct
{
    int         n_planes;
    int         n_rows;
    int         n_columns;
    double***   array;

}   Cube;

A CSV file for example is char ** ** , a sheet workbook is char ** ** ** and it is a bit scary, but works. For each ** a counter is needed, as said above about main()

AC example

The code below uses arr , declared as double*** , to

  • store a pointer to a pointer to a pointer to a double
    • prints the value using the 3 pointers
  • then uses arr again to build a cube of X*Y*Z doubles, using a bit of math to set values to 9XY9.Z9
    • the program uses 2, 3 and 4 for a total of 24 values
    • lists the full array
    • list the first and the very last element, arr[0][0][0] and arr[X-1][Y-1][Z-1]
    • frees the whole thing in reverse order

The code

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

typedef struct
{
    int         n_planes;
    int         n_rows;
    int         n_columns;
    double***   array;

}   Cube;

int         print_array(double***, int, int, int);

int main(void)
{

    double   sample = 20.21;
    double*  pDouble = &sample;
    double** ppDouble = &pDouble;

    double*** arr = &ppDouble;

    printf("***arr is %.2ff\n", ***arr);
    printf("original double is %.2ff\n", sample);
    printf("*pDouble is %.2ff\n", *pDouble);
    printf("**ppDouble is %.2ff\n", **ppDouble);

    // but we can build a cube of XxYxZ doubles for arr
    int  X = 2;
    int  Y = 3;
    int  Z = 4; // 24 elements

    arr = (double***)malloc(X * sizeof(double**));
    // now each arr[i] must point to an array of double**
    for (int i = 0; i < X; i += 1)
    {
        arr[i] = (double**)malloc(Y * sizeof(double*));
        for (int j = 0; j < Y; j += 1)
        {
            arr[i][j] = (double*)malloc(Z * sizeof(double));
            for (int k = 0; k < Z; k += 1)
            {
                arr[i][j][k] = (100. * i) + (10. * j) + (.1 * k) + 9009.09;
            }
        }
    }
    print_array(arr, X, Y, Z);
    printf("\n\
Test: first element is arr[%d][%d[%d] = %6.2f (9XY9.Z9)\n\
       last element is arr[%d][%d[%d] = %6.2f (9XY9.Z9)\n",
        0, 0, 0, arr[0][0][0],
        (X-1), (Y-1), (Z-1), arr[X-1][Y-1][Z-1]
    );

    // now to free this monster
    for (int x = 0; x < X; x += 1)
    {
        for (int y = 0; y < Y; y += 1)
        {
            free(arr[x][y]); // the Z rows
        }
        free(arr[x]); // the plane Y
    }
    free(arr); // the initial pointer;
    return 0;
};  // main()

int         print_array(double*** block, int I, int J, int K)
{
    for (int a = 0; a < I; a += 1)
    {
        printf("\nPlane %d\n\n", a);
        for (int b = 0; b < J; b += 1)
        {
            for (int c = 0; c < K; c += 1)
            {
                printf("%6.2f ", block[a][b][c]);
            }
            printf("\n");
        }
    }
    return 0;
};  // print_array()

The output

***arr is 20.21f
original double is 20.21f
*pDouble is 20.21f
**ppDouble is 20.21f

Plane 0

9009.09 9009.19 9009.29 9009.39
9019.09 9019.19 9019.29 9019.39
9029.09 9029.19 9029.29 9029.39

Plane 1

9109.09 9109.19 9109.29 9109.39
9119.09 9119.19 9119.29 9119.39
9129.09 9129.19 9129.29 9129.39 

Test: first element is arr[0][0[0] = 9009.09 (9XY9.Z9)
       last element is arr[1][2[3] = 9129.39 (9XY9.Z9)

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