Reputation: 2851
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
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
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