Pavan
Pavan

Reputation: 2851

Passing Pointer to Pointer to functions

void foo(double **A, double **B) {
    //....   
}

int main() {  
    double **A;
    double B[10][10];
    int i, j;

    A = (double **)malloc(sizeof(double*) * 10);        

    for (i = 0; i < 10; i++) {
        *(A+i) = (double*)malloc(sizeof(double) * 10);
        for (j = 0; j < 10; j++) {
            *(*(A+i)+j) = i * j;
        }
    }

    foo(A, B);

    return 0;
}

This gives me a warning

warning: incompatible pointer types passing 'double [10][10]' to parameter of type 'double **'
  [-Wincompatible-pointer-types]

From my understanding B holds a pointer to a pointer of type double. Am I not doing the same thing with A and B. Why am I getting this warning only for B?

Upvotes: 0

Views: 86

Answers (3)

David C. Rankin
David C. Rankin

Reputation: 84642

The primary difference between passing a pointer to array and pointer to pointer from a syntax standpoint isn't too difficult to understand. When you pass a pointer to array, either array[x][y] or (*array)[y], you specify the parameter as either:

somefunc (type array[][y])

or

somefunc (type (*array)[y])

The rule to take away -- when passing a pointer to array, you must always pass the number of columns involved.

On the other hand, when passing a pointer-to-pointer-to-type, you only need to pass a pointer. e.g.:

somefunc (type **array)

The primary difference in "Why?" has to do with how the information is stored in memory. Take for example int array[x][y]. There you have x * y integers stored in a sequential block of memory. x and y provide a direct index to an integer within that sequential block. (anywhere within the x arrays containing y values each).

On the other hand, with int **array, you have a pointer to pointer -- meaning that your array[x] value identifies points to another pointer holding the beginning address of an array of y values. There is no requirement that any of the separate pointers identified by array[0], array[1], ... be stored in any sequential manner.

Take the following example. You have array (your typical array[x][y] or 2D array as it is often referred to) and arraydp an array of pointer-to-pointer-to-type (your typical double-pointer). The example shows the different way you must handle passing each.

The tweak in the game is that a function can only return a single value (a pointer), so to return a reference to array[x][y] it must be returned as a double-pointer and recast appropriately.

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

#define MAX 5

/* function prototypes */
int **alloc_fill (size_t n);
int **alloc_fill_dp (size_t n);
void prn_array (int (*a)[MAX], size_t nrow);
void prn_array_dp (int **a, size_t nrow, size_t ncol);

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

    int (*array)[MAX] = { NULL };   /* pointer to array of MAX ints */
    int **arraydp = NULL;           /* pointer to pointer to int    */
    size_t i, n;

    n = argc > 1 ? atoi(argv[1]) : 5;   /* set number of rows */

    /* fill 'n' pointer to array[MAX] */
    array   = (int (*)[MAX])alloc_fill (n);

    /* fill 'n' pointer to pointer to int */
    arraydp = alloc_fill_dp (n);

    if (!array || !arraydp ) { /* validate both allocated */
        fprintf (stderr, "error: alloc_fill failed.\n");
        return 1;
    }

    printf ("\n elements of '%zu' arrays:\n\n", n);
    prn_array (array, n);
    printf ("\n elements of '%zu' arrays:\n\n", n);
    prn_array_dp (arraydp, n, MAX);

    free (array);           /* single call to free for 'array' */

    for (i = 0; i < n; i++) /* free each pointer, then arraydp */
        free (arraydp[i]);
    free (arraydp);

    return 0;
}

/* allocate/fill 'n' pointer to array of MAX int */
   int **alloc_fill (size_t n)
{
    int (*a)[MAX] = { NULL };
    size_t i, j;

    if (!(a = calloc (n, sizeof **a * MAX))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return NULL;
    }

    for (i = 0; i < n; i++)
        for (j = 0; j < MAX; j++)
            a[i][j] = (i + 1) * (j + 1);

    return (int **)a;
}

/* allocate/fill 'n' pointer to pointer to type int */
   int **alloc_fill_dp (size_t n)
{
    int **a = NULL;
    size_t i, j;

    /* allocate 'n' pointers */
    if (!(a = calloc (n, sizeof *a))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return NULL;
    }

    for (i = 0; i < n; i++) {
        /* allocate MAX ints */
        if (!(a[i] = calloc (MAX, sizeof **a))) {
            fprintf (stderr, "error: virtual memory exhausted.\n");
            return NULL;
        }
        for (j = 0; j < MAX; j++)
            a[i][j] = (i + 1) * (j + 1);
    }

    return a;
}

/* print function for 'nrow' pointers
 * to array of 'MAX' ints
 */
void prn_array (int (*a)[MAX], size_t nrow)
{
    size_t i,j;

    for (i = 0; i < nrow; i++) {
        for (j = 0; j < MAX; j++)
            printf (" %4d", a[i][j]);
        // putchar ('\n');
        putchar ('\n'), putchar ('\n');
    }
}

/* printf function for 'nrow' pointers
 * to pointer to 'ncol' ints
 */
void prn_array_dp (int **a, size_t nrow, size_t ncol)
{
    size_t i,j;

    for (i = 0; i < nrow; i++) {
        for (j = 0; j < ncol; j++)
            printf (" %4d", a[i][j]);
        // putchar ('\n');
        putchar ('\n'), putchar ('\n');
    }
}

Output

$ ./bin/array_ptr_to_array

 elements of '5' arrays:

    1    2    3    4    5

    2    4    6    8   10

    3    6    9   12   15

    4    8   12   16   20

    5   10   15   20   25


 elements of '5' arrays:

    1    2    3    4    5

    2    4    6    8   10

    3    6    9   12   15

    4    8   12   16   20

    5   10   15   20   25

Difference of Storage in Memory

Here in memory is where the rubber meets the road. I you look below, you have the debugger (gdb) depiction of the memory layout for both array and arraydp. Notice with array all values are sequential. However, with arraydp, the first 5 values are the pointer address that point to each of the respective 5 int arrays that make up the values for arraydp. If you then examine pointer address for arraydp[0-4], you then may index each of the individual values:

array in memory:

(gdb) x/25d array
0x603010:       1       2       3       4
0x603020:       5       2       4       6
0x603030:       8       10      3       6
0x603040:       9       12      15      4
0x603050:       8       12      16      20
0x603060:       5       10      15      20
0x603070:       25

arraydp in memory:

(gdb) x/49d arraydp
0x603080:       6303920 0       6303952 0
0x603090:       6303984 0       6304016 0
0x6030a0:       6304048 0       33      0
0x6030b0:       1       2       3       4
0x6030c0:       5       0       33      0
0x6030d0:       2       4       6       8
0x6030e0:       10      0       33      0
0x6030f0:       3       6       9       12
0x603100:       15      0       33      0
0x603110:       4       8       12      16
0x603120:       20      0       33      0
0x603130:       5       10      15      20
0x603140:       25

(gdb) x/5d 6303920
0x6030b0:       1       2       3       4
0x6030c0:       5

(gdb) x/5d 6303952
0x6030d0:       2       4       6       8
0x6030e0:       10

(gdb) x/5d 6303984
0x6030f0:       3       6       9       12
0x603100:       15

(gdb) x/5d 6304016
0x603110:       4       8       12      16
0x603120:       20

(gdb) x/5d 6304048
0x603130:       5       10      15      20
0x603140:       25

From a programming standpoint, the differences may seem subtle, but they are critical from a syntax standpoint. Look it over and let me know if you have further questions.

Upvotes: 1

Neil Roy
Neil Roy

Reputation: 632

Try void foo(double **A, double B[10][10]) then pass it foo(A, B)

Upvotes: 1

chqrlie
chqrlie

Reputation: 145287

B is an array of arrays of 10 double, a very different type from an array of pointers to arrays of double. Change foo's prototype to:

void foo(double **A, double (*B)[10])

Also simplify the code in main this way:

int main() {
    double **A;
    double B[10][10];
    int i, j;

    A = malloc(10 * sizeof(*A));
    for (i = 0; i < 10; i++) {
        A[i] = malloc(10 * sizeof(*A[i]));
        for (j = 0; j < 10; j++) {
            A[i][j] = i * j;
        }
    }
    foo(A, B);
    return 0;
}

Upvotes: 2

Related Questions