Pointer-to-pointer-to-pointer question

M

masood.iqbal

The code example below shows the dynamic allocation of a 2D array. I
must admit that it took quite a while for me to get there (I already
have another posting to that effect), but I am glad that I finally got
it working. Now here's the problem:

I am able to get the 2D array dynamically allocated correctly as long
as I am doing it "in-line" (i.e. without invoking any function). The
moment I try to do it in another function, I get a core dump. Any help
will be appreciated.

Since this function is expected to update a pointer-to-pointer type, I
am actually passing the pointer-to-pointer-to-pointer type to the
function. What am I missing here?

You can see that the source code works correctly when I am perform 2D
array initialization "in-line" (i.e. by not invoking a function
call) simply by un-commenting the line

/* #define INLINE_INIT */

Masood

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

/*#define INLINE_INIT*/

#define MAXROWS 3
#define MAXCOLS 5


void
buildTbl(int ***tblPtr, size_t numRows, size_t numCols)
{
*tblPtr = (int **)malloc(numRows*sizeof(int*));
/* C++ : *tblPtr = new (int*)[numRows]; */

for(size_t i = 0; i < numCols; i++)
*tblPtr = (int *)malloc(numCols*sizeof(int));
/* C++: *tblPtr = new (int)[numCols]; */
}


main()
{
int startVal = 5;

int **tbl;

#ifdef INLINE_INIT
tbl = (int **)malloc(MAXROWS*sizeof(int*));
/* C++ : tbl = new (int*)[MAXROWS]; */

for(size_t i = 0; i < MAXCOLS; i++)
tbl = (int *)malloc(MAXCOLS*sizeof(int));
/* C++: tbl = new (int)[MAXCOLS]; */
#else
buildTbl(&tbl, MAXROWS, MAXCOLS);
#endif

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
tbl[row][col] = startVal++;

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
printf("Row: %d, Col: %d => %d\n",
row, col, tbl[row][col]);
return 0;
}
 
T

Tobias Oed

Masood

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

/*#define INLINE_INIT*/

#define MAXROWS 3
#define MAXCOLS 5


void
buildTbl(int ***tblPtr, size_t numRows, size_t numCols)
{
*tblPtr = (int **)malloc(numRows*sizeof(int*));
/* C++ : *tblPtr = new (int*)[numRows]; */

for(size_t i = 0; i < numCols; i++)
*tblPtr = (int *)malloc(numCols*sizeof(int));


(*tblPtr) = ...

Note that x = malloc(n * sizeof *x);
is preferred to x = (type *) malloc(n * sizeof(type);
You should check that malloc doesn't return NULL.
You could change the return type of buildTbl to int ** and
return the array that way instead of through the parameter.

Do I remember that stuff Ok? Nice to see most of you are
still here!

Tobias
/* C++: *tblPtr = new (int)[numCols]; */
}


main()
{
int startVal = 5;

int **tbl;

#ifdef INLINE_INIT
tbl = (int **)malloc(MAXROWS*sizeof(int*));
/* C++ : tbl = new (int*)[MAXROWS]; */

for(size_t i = 0; i < MAXCOLS; i++)
tbl = (int *)malloc(MAXCOLS*sizeof(int));
/* C++: tbl = new (int)[MAXCOLS]; */
#else
buildTbl(&tbl, MAXROWS, MAXCOLS);
#endif

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
tbl[row][col] = startVal++;

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
printf("Row: %d, Col: %d => %d\n",
row, col, tbl[row][col]);
return 0;
}
 
M

Michael Mair

The code example below shows the dynamic allocation of a 2D array. I
must admit that it took quite a while for me to get there (I already
have another posting to that effect), but I am glad that I finally got
it working. Now here's the problem:

I am able to get the 2D array dynamically allocated correctly as long
as I am doing it "in-line" (i.e. without invoking any function). The
moment I try to do it in another function, I get a core dump. Any help
will be appreciated.

Since this function is expected to update a pointer-to-pointer type, I
am actually passing the pointer-to-pointer-to-pointer type to the
function. What am I missing here?

You can get lost with the intricacies of C.
One "trick" to get around it: Use a local "pointer-to-pointer type"
variable to do as you always did and assign its value to the object
your "pointer-to-pointer-to-pointer type" argument points to.
-> Essentially the same code, one additional line.
(You perform all operations on *tblPtr which essentially means that
you could also work with a int **tbl and put *tblPtr=tbl at the end
if everything worked out)
If you follow Tobias's advice, it becomes even easier.
You can see that the source code works correctly when I am perform 2D
array initialization "in-line" (i.e. by not invoking a function
call) simply by un-commenting the line

/* #define INLINE_INIT */

Masood
[snip: Code]

Tobias Oed has already mentioned everything I can see.
Follow _every_ point of his advice regarding malloc().

BTW: Thank you for providing a good minimal example!


Cheers
Michael
 
C

Christian Kandeler

void
buildTbl(int ***tblPtr, size_t numRows, size_t numCols)
{
*tblPtr = (int **)malloc(numRows*sizeof(int*));
/* C++ : *tblPtr = new (int*)[numRows]; */

for(size_t i = 0; i < numCols; i++)

The condition should be i < numRows.
*tblPtr = (int *)malloc(numCols*sizeof(int));


This will access memory not belonging to tblPtr if i is greater than 0.
Change it to the following:
(*tblPtr) = malloc(numCols*sizeof(int));
It's also a good idea to check the return value of malloc().


Christian
 
A

Amit

/* #define INLINE_INIT */

Masood
void
buildTbl(int ***tblPtr, size_t numRows, size_t numCols)
{
*tblPtr = (int **)malloc(numRows*sizeof(int*));
/* C++ : *tblPtr = new (int*)[numRows]; */

for(size_t i = 0; i < numCols; i++)
*tblPtr = (int *)malloc(numCols*sizeof(int));

(*tblPtr) = (int *)malloc(numCols*sizeof(int)); <------
/* C++: *tblPtr = new (int)[numCols]; */
}


I am not a master of C, but what i can see is the problem of precedence.
And yes you should check the return value of malloc also.
 
J

Jonathan Burd

The code example below shows the dynamic allocation of a 2D array. I
must admit that it took quite a while for me to get there (I already
have another posting to that effect), but I am glad that I finally got
it working. Now here's the problem:

I am able to get the 2D array dynamically allocated correctly as long
as I am doing it "in-line" (i.e. without invoking any function). The
moment I try to do it in another function, I get a core dump. Any help
will be appreciated.

Since this function is expected to update a pointer-to-pointer type, I
am actually passing the pointer-to-pointer-to-pointer type to the
function. What am I missing here?

You can see that the source code works correctly when I am perform 2D
array initialization "in-line" (i.e. by not invoking a function
call) simply by un-commenting the line

/* #define INLINE_INIT */

Masood

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

/*#define INLINE_INIT*/

#define MAXROWS 3
#define MAXCOLS 5


void
buildTbl(int ***tblPtr, size_t numRows, size_t numCols)
{

Return an int** instead.
*tblPtr = (int **)malloc(numRows*sizeof(int*));
/* C++ : *tblPtr = new (int*)[numRows]; */

for(size_t i = 0; i < numCols; i++)
*tblPtr = (int *)malloc(numCols*sizeof(int));
/* C++: *tblPtr = new (int)[numCols]; */
}


Instead of allocating memory so many times, a better method
would be to call malloc only once to allocate all the
memory you need. This would improve the efficiency of
your code. (See example below.)

int main (void)... is better.
int startVal = 5;

int **tbl;

#ifdef INLINE_INIT
tbl = (int **)malloc(MAXROWS*sizeof(int*));

Don't cast malloc or any other function that
returns a pointer unless you really know what you are
doing.

Should be tbl = malloc(...);
/* C++ : tbl = new (int*)[MAXROWS]; */

for(size_t i = 0; i < MAXCOLS; i++)

for (size_t i ... ) won't work with C'89.
Avoid it.
tbl = (int *)malloc(MAXCOLS*sizeof(int));


See above.
/* C++: tbl = new (int)[MAXCOLS]; */
#else
buildTbl(&tbl, MAXROWS, MAXCOLS);
#endif

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
tbl[row][col] = startVal++;

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
printf("Row: %d, Col: %d => %d\n",
row, col, tbl[row][col]);
return 0;
}


I've seen code that did not have proper braces
to block the scope of such for loops. The problem
I faced was that the formatting of the code was
lost and the code that followed that for
statement was a large "if statement".
Let me tell you, it wasn't easy to reformat that
code manually. Properly placing braces can save you a lot of time.
Use them.

Additionally, I don't see why, after you've been told before,
you don't use a proper NG client to post. It is *difficult*
to read unindented code. Please use a good client.


/* grid_alloc.c. */
#include <stdlib.h>

int **grid_alloc (size_int rows, size_int cols)
{
int **mem; /* allocated memory */
int *p; /* temporary ptr */
size_int r; /* row index */

mem = malloc (rows * cols * sizeof(int) + rows * sizeof(*mem));
if(mem != NULL)
{
/* memory layout:
* [ pointers: one pointer to each row of data ][ data ]
*/
for (r = 0, p = (int*)mem + rows; r < rows; ++r, p += cols)
{
mem[r] = p;
}
}

return mem;
}

/* ... */

This allows you to use array syntax to address elements.
Don't forget to use free() when you're done with it, however.

Regards,
Jonathan.
 
J

Jonathan Burd

Jonathan said:
The code example below shows the dynamic allocation of a 2D array. I
must admit that it took quite a while for me to get there (I already
have another posting to that effect), but I am glad that I finally got
it working. Now here's the problem:

I am able to get the 2D array dynamically allocated correctly as long
as I am doing it "in-line" (i.e. without invoking any function). The
moment I try to do it in another function, I get a core dump. Any help
will be appreciated.

Since this function is expected to update a pointer-to-pointer type, I
am actually passing the pointer-to-pointer-to-pointer type to the
function. What am I missing here?

You can see that the source code works correctly when I am perform 2D
array initialization "in-line" (i.e. by not invoking a function
call) simply by un-commenting the line

/* #define INLINE_INIT */

Masood

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

/*#define INLINE_INIT*/

#define MAXROWS 3
#define MAXCOLS 5


void
buildTbl(int ***tblPtr, size_t numRows, size_t numCols)
{


Return an int** instead.
*tblPtr = (int **)malloc(numRows*sizeof(int*));
/* C++ : *tblPtr = new (int*)[numRows]; */

for(size_t i = 0; i < numCols; i++)
*tblPtr = (int *)malloc(numCols*sizeof(int));
/* C++: *tblPtr = new (int)[numCols]; */
}



Instead of allocating memory so many times, a better method
would be to call malloc only once to allocate all the
memory you need. This would improve the efficiency of
your code. (See example below.)


int main (void)... is better.
int startVal = 5;

int **tbl;

#ifdef INLINE_INIT
tbl = (int **)malloc(MAXROWS*sizeof(int*));


Don't cast malloc or any other function that
returns a pointer unless you really know what you are
doing.

Should be tbl = malloc(...);
/* C++ : tbl = new (int*)[MAXROWS]; */

for(size_t i = 0; i < MAXCOLS; i++)


for (size_t i ... ) won't work with C'89.
Avoid it.
tbl = (int *)malloc(MAXCOLS*sizeof(int));



See above.
/* C++: tbl = new (int)[MAXCOLS]; */
#else
buildTbl(&tbl, MAXROWS, MAXCOLS);
#endif

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
tbl[row][col] = startVal++;

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
printf("Row: %d, Col: %d => %d\n",
row, col, tbl[row][col]);
return 0;
}


I've seen code that did not have proper braces
to block the scope of such for loops. The problem
I faced was that the formatting of the code was
lost and the code that followed that for
statement was a large "if statement".
Let me tell you, it wasn't easy to reformat that
code manually. Properly placing braces can save you a lot of time.
Use them.

Additionally, I don't see why, after you've been told before,
you don't use a proper NG client to post. It is *difficult*
to read unindented code. Please use a good client.


/* grid_alloc.c. */
#include <stdlib.h>

int **grid_alloc (size_int rows, size_int cols)
{
int **mem; /* allocated memory */
int *p; /* temporary ptr */
size_int r; /* row index */


Typos. Should be size_int should be size_t. The editor
I was using to search-n-replace goofed. :|
mem = malloc (rows * cols * sizeof(int) + rows * sizeof(*mem));
if(mem != NULL)
{
/* memory layout:
* [ pointers: one pointer to each row of data ][ data ]
*/
for (r = 0, p = (int*)mem + rows; r < rows; ++r, p += cols)
{
mem[r] = p;
}
}

return mem;
}

/* ... */

This allows you to use array syntax to address elements.
Don't forget to use free() when you're done with it, however.

Regards,
Jonathan.
 
B

Barry Schwarz

The code example below shows the dynamic allocation of a 2D array. I
must admit that it took quite a while for me to get there (I already
have another posting to that effect), but I am glad that I finally got
it working. Now here's the problem:

I am able to get the 2D array dynamically allocated correctly as long
as I am doing it "in-line" (i.e. without invoking any function). The
moment I try to do it in another function, I get a core dump. Any help
will be appreciated.

Since this function is expected to update a pointer-to-pointer type, I
am actually passing the pointer-to-pointer-to-pointer type to the
function. What am I missing here?

You can see that the source code works correctly when I am perform 2D
array initialization "in-line" (i.e. by not invoking a function
call) simply by un-commenting the line

/* #define INLINE_INIT */

Masood

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

/*#define INLINE_INIT*/

#define MAXROWS 3
#define MAXCOLS 5


void
buildTbl(int ***tblPtr, size_t numRows, size_t numCols)
{
*tblPtr = (int **)malloc(numRows*sizeof(int*));
/* C++ : *tblPtr = new (int*)[numRows]; */

for(size_t i = 0; i < numCols; i++)
*tblPtr = (int *)malloc(numCols*sizeof(int));


This line is interpreted as
*(tblPtr) = ...

You want
(*tblPtr) = ...

The parentheses are not optional.
/* C++: *tblPtr = new (int)[numCols]; */
}


main()
{
int startVal = 5;

int **tbl;

#ifdef INLINE_INIT
tbl = (int **)malloc(MAXROWS*sizeof(int*));
/* C++ : tbl = new (int*)[MAXROWS]; */

for(size_t i = 0; i < MAXCOLS; i++)
tbl = (int *)malloc(MAXCOLS*sizeof(int));
/* C++: tbl = new (int)[MAXCOLS]; */
#else
buildTbl(&tbl, MAXROWS, MAXCOLS);
#endif

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
tbl[row][col] = startVal++;

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
printf("Row: %d, Col: %d => %d\n",
row, col, tbl[row][col]);
return 0;
}




<<Remove the del for email>>
 
B

Barry Schwarz

The code example below shows the dynamic allocation of a 2D array. I
must admit that it took quite a while for me to get there (I already
have another posting to that effect), but I am glad that I finally got
it working. Now here's the problem:

I am able to get the 2D array dynamically allocated correctly as long
as I am doing it "in-line" (i.e. without invoking any function). The
moment I try to do it in another function, I get a core dump. Any help
will be appreciated.

Since this function is expected to update a pointer-to-pointer type, I
am actually passing the pointer-to-pointer-to-pointer type to the
function. What am I missing here?

You can see that the source code works correctly when I am perform 2D
array initialization "in-line" (i.e. by not invoking a function
call) simply by un-commenting the line

/* #define INLINE_INIT */

Masood

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

/*#define INLINE_INIT*/

#define MAXROWS 3
#define MAXCOLS 5


void
buildTbl(int ***tblPtr, size_t numRows, size_t numCols)
{
*tblPtr = (int **)malloc(numRows*sizeof(int*));
/* C++ : *tblPtr = new (int*)[numRows]; */

for(size_t i = 0; i < numCols; i++)
*tblPtr = (int *)malloc(numCols*sizeof(int));


This line is interpreted as
*(tblPtr) = ...

You want
(*tblPtr) = ...

The parentheses are not optional.
/* C++: *tblPtr = new (int)[numCols]; */
}


main()
{
int startVal = 5;

int **tbl;

#ifdef INLINE_INIT
tbl = (int **)malloc(MAXROWS*sizeof(int*));
/* C++ : tbl = new (int*)[MAXROWS]; */

for(size_t i = 0; i < MAXCOLS; i++)
tbl = (int *)malloc(MAXCOLS*sizeof(int));
/* C++: tbl = new (int)[MAXCOLS]; */
#else
buildTbl(&tbl, MAXROWS, MAXCOLS);
#endif

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
tbl[row][col] = startVal++;

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
printf("Row: %d, Col: %d => %d\n",
row, col, tbl[row][col]);
return 0;
}




<<Remove the del for email>>
 
B

Barry Schwarz

The code example below shows the dynamic allocation of a 2D array. I
must admit that it took quite a while for me to get there (I already
have another posting to that effect), but I am glad that I finally got
it working. Now here's the problem:

I am able to get the 2D array dynamically allocated correctly as long
as I am doing it "in-line" (i.e. without invoking any function). The
moment I try to do it in another function, I get a core dump. Any help
will be appreciated.

Since this function is expected to update a pointer-to-pointer type, I
am actually passing the pointer-to-pointer-to-pointer type to the
function. What am I missing here?

You can see that the source code works correctly when I am perform 2D
array initialization "in-line" (i.e. by not invoking a function
call) simply by un-commenting the line

/* #define INLINE_INIT */

Masood

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

/*#define INLINE_INIT*/

#define MAXROWS 3
#define MAXCOLS 5


void
buildTbl(int ***tblPtr, size_t numRows, size_t numCols)
{
*tblPtr = (int **)malloc(numRows*sizeof(int*));
/* C++ : *tblPtr = new (int*)[numRows]; */

for(size_t i = 0; i < numCols; i++)
*tblPtr = (int *)malloc(numCols*sizeof(int));


This line is interpreted as
*(tblPtr) = ...

You want
(*tblPtr) = ...

The parentheses are not optional.
/* C++: *tblPtr = new (int)[numCols]; */
}


main()
{
int startVal = 5;

int **tbl;

#ifdef INLINE_INIT
tbl = (int **)malloc(MAXROWS*sizeof(int*));
/* C++ : tbl = new (int*)[MAXROWS]; */

for(size_t i = 0; i < MAXCOLS; i++)
tbl = (int *)malloc(MAXCOLS*sizeof(int));
/* C++: tbl = new (int)[MAXCOLS]; */
#else
buildTbl(&tbl, MAXROWS, MAXCOLS);
#endif

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
tbl[row][col] = startVal++;

for(size_t row = 0; row < MAXROWS; row++)
for(size_t col = 0; col < MAXCOLS; col++)
printf("Row: %d, Col: %d => %d\n",
row, col, tbl[row][col]);
return 0;
}




<<Remove the del for email>>
 
C

Chris Torek

[Side note: the article to which I am posting a follow-up got posted
three times, with three different message-IDs. Software glitch, I
presume.]
for(size_t i = 0; i < numCols; i++)
*tblPtr = (int *)malloc(numCols*sizeof(int));


This line is interpreted as
*(tblPtr) = ...

You want
(*tblPtr) = ...

The parentheses are not optional.


Right. Note, however, that if for some reason one finds the
parenthesized form objectionable, (*p) can be rewritten as
p[0]. (Likewise, p->field can be rewritten as p[0].field.)

I think this particular rewrite is misleading and would avoid
it myself. In fact, I would probably use code of the form:

nonvoid_return_type some_fn(T ***ptable, ...) {
T **table;

... do all the local work with table and table ...

*ptable = table;
return other_useful_return_value;
}

or, as in this particular case (where the function had "void" as
its return type), just return a "T **" value and eliminate the
three-levels-deep pointer argument.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,999
Messages
2,570,243
Members
46,835
Latest member
lila30

Latest Threads

Top