0
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?

1
  • "From my understanding B holds a pointer to a pointer of type double" - actually B is an array. See how there are no * symbols in its declaration. Arrays and pointers are different. Commented Jan 15, 2016 at 6:51

3 Answers 3

2

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;
}
Sign up to request clarification or add additional context in comments.

11 Comments

sizeof is not a function. sizeof *A and sizeof *A[i] You still get a vote.
@synthetel: sizeof is indeed an operator and parentheses could be elided for an expression operand, but I prefer to always parenthesize the operand for consistency. I particularly dislike this syntax: malloc(10 * sizeof * A[i]), not to mention its ugly sibling: malloc(sizeof * A[i] * 10)
Ok. But malloc(n * sizeof *A[i]) and malloc(sizeof *A[i] * n) and malloc(n * (sizeof *A[i])) and malloc((sizeof *A[i]) * n)
These do not appeal to me any more than malloc(n * sizeof*A[i]) or malloc(sizeof*A[i] * n). I am quite anal about style. I like to have the function style syntax for sizeof for consistency with countof too.
@synthetel Do you really prefer (sizeof thing) to sizeof(thing)?
|
1

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.

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.