🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: comp.lang.c.moderated
3 messages
3 total messages Started by Arun M. Thomas Sat, 06 Apr 1996 14:58
(double **) void **
#2530
Author: Arun M. Thomas
Date: Sat, 06 Apr 1996 14:58
55 lines
2397 bytes
Greetings.  

I'm trying to make a function to generate 2D arrays.  One of the arguments to
my function is the size of the datatype to be stored as one element in the 
array.  The function is listed below, and I'd like to know if it is legal.
I'm afraid it isn't (I've commented at the lines where I'm doubtful.)  

Note:
I did check the FAQ, and didn't find anything specifically mentioning this.
If I still seem to have overlooked something, a pointer to the appropriate 
item will be enough as a response, though more is always welcome.

Thanks in advance, 
-Arun Thomas

/* ========================================================================= */


void **matrix(long len_d1, long len_d2, const size_t tsize)
{ /* allocate a matrix with subscript range [0..len_d1-1][0..len_d2-1] */

    void **m;

    m = (void **) calloc(len_d1, sizeof(void *));        /* Is (void **) a   */
    if(!m) nrerror("allocation failure 1 in matrix()");  /*  valid date type?*/

    for(len_d1--; 0 <= len_d1; len_d1--) {
	m[len_d1] = (void *) calloc(len_d2, tsize); 
	if (!m[len_d1]) nrerror("allocation failure 2 in matrix()");
    }
}

int main(void)
{
   double **m;

   m = (double **) matrix(10, 10, sizeof(double)); 

   /* The above line casts (void **) to (double **) */

   m[5][5] = 1.0; /* Is this a safe command? I'm not sure if it makes sense 
                     to dereference m.  m[5] should point to a (double *), 
                     but when it was assigned, it pointed to a (void *).  Does 
                     the type cast above have an effect at both levels?      */

   return(1);
}

/* ========================================================================= */

Upon re-reading the above, I realize my question may not be very clear.  Yet, 
I'm not sure how to explain it better.  Again, I would appreciate any help, 
even if only a suggestion as what more information might be needed to better
explain this question.  
Re: (double **) void **
#2545
Author: William Kaufman
Date: Mon, 08 Apr 1996 16:07
33 lines
1928 bytes
In article <4k60po$aic@solutions.solon.com> thomas@eel.rdc.toshiba.co.jp (Arun M. Thomas) writes:
] 
] Greetings.  
] 
] I'm trying to make a function to generate 2D arrays.  One of the arguments to
] my function is the size of the datatype to be stored as one element in the 
] array.  The function is listed below, and I'd like to know if it is legal.
] I'm afraid it isn't (I've commented at the lines where I'm doubtful.)  

    From the comp.lang.c FAQ:

4.9:	Can I use a void ** pointer to pass a generic pointer to a
	function by reference?

A:	Not portably.  There is no generic pointer-to-pointer type in C.
	void * acts as a generic pointer only because conversions are
	applied automatically when other pointer types are assigned to
	and from void *'s; these conversions cannot be performed (the
	correct underlying pointer type is not known) if an attempt is
	made to indirect upon a void ** value which points at something
	other than a void *.

]     m = (void **) calloc(len_d1, sizeof(void *));        /* Is (void **) a   */
]     if(!m) nrerror("allocation failure 1 in matrix()");  /*  valid date type?*/

    (void **) _is_ a valid data type, but it isn't worth much unless you
actually want an array of (void *)s and will use it as such.


                                           -- Bill K.

Bill Kaufman,          | "People look better under arrest,...it gives you
wkaufman@us.oracle.com |  an aggressive fashion look."  -- John Waters
Re: (double **) void **
#2544
Author: Kazimir Kylheku
Date: Mon, 08 Apr 1996 16:07
118 lines
5095 bytes
In article <4k60po$aic@solutions.solon.com>,
Arun M. Thomas <thomas@eel.rdc.toshiba.co.jp> wrote:
>
>Greetings.  
>
>I'm trying to make a function to generate 2D arrays.  One of the arguments to
>my function is the size of the datatype to be stored as one element in the 
>array.  The function is listed below, and I'd like to know if it is legal.
>I'm afraid it isn't (I've commented at the lines where I'm doubtful.)  

You can't generate arrays at run-time in C, except auto arrays that are created
and destroyed along with a function's activation record. Dynamically allocated
blocks are not arrays proper. You can't overlay an array lvalue on top of
malloc-ed spacce.

>void **matrix(long len_d1, long len_d2, const size_t tsize)
>{ /* allocate a matrix with subscript range [0..len_d1-1][0..len_d2-1] */

void ** isn't a generic pointer the way void * is.

>    void **m;
>
>    m = (void **) calloc(len_d1, sizeof(void *));        /* Is (void **) a   */

This is weird. You are allocating a block of void pointers. Ok.

>    if(!m) nrerror("allocation failure 1 in matrix()");  /*  valid date type?*/
>
>    for(len_d1--; 0 <= len_d1; len_d1--) {
>	m[len_d1] = (void *) calloc(len_d2, tsize); 

And now you have assigned to each pointer some space. OK.

>	if (!m[len_d1]) nrerror("allocation failure 2 in matrix()");
>    }
>}
>
>int main(void)
>{
>   double **m;
>
>   m = (double **) matrix(10, 10, sizeof(double)); 
>
>   /* The above line casts (void **) to (double **) */

That is wrong. See, just because you have cast the pointer to a block of void
ponters to a pointer to a block of pointers to double doesn't mean that that
block of void pointers will magically be converted into double pointers, or
even that the pointer arithmetic for double ** makes sense for accessing a block
of void ** pointers! 

This is analogous to making an array or allocated block of type char, and then
using a long * pointer to reference it, rather than using a char * pointer to
fetch the actual element and converting _that_ to type long:


>   m[5][5] = 1.0; /* Is this a safe command? I'm not sure if it makes sense 
>                     to dereference m.  m[5] should point to a (double *), 
>                     but when it was assigned, it pointed to a (void *).  Does 
>                     the type cast above have an effect at both levels?      */

No, it isn't. You should treat the return from matrix() as a pointer to a block
of void *'s, and then cast these _individually_ to whatever pointer type you
need. You need to get at the malloced void *'s first, and then coerce these
into whatever type you need. Note that you can only do this latter operation
because you know that the void pointers come from malloc, and are therefore
suitably aligned so that they can be converted to a pointer to any type.

void **m = matrix( .... );

((double *) m[1])[1] = 1.0;

Here, I first retrieve the second void * from the allocated array, and then
properly cast it to a double *, so I am sure I got the right reference. What
you are doing is essentially this:

((double **) m)[1][1] = 1.0;

On a machine where double pointers have a different size from void pointers,
the very reference will fail. On a machine where void pointers have the same
size, but different representation from double pointers, it will still fail
because the compiler will not do the conversion, it will simply reinterpret
the void *'s as double *'s, bit for bit---the compiler's contract to you is
finished as soon as it does the void ** -> double ** conversion.

>   return(1);

Returning 1 is a poor idea. A successful exit status is indicated using
``return 0;'' or ``return EXIT_SUCESS;''. A failure is indicated by returning
``EXIT_FAILURE''.


You might simplify your ``array creator'' routine by implementing it as a
macro. The nice thing about macros is that you can pass them a type; they
can expand into type-specific primitives

#define matrix(type, ptr, rows, cols) {					\
	int I;								\
	if (ptr = malloc((rows) * sizeof ((type) *) )) {		\
		for (I = 0; I < (rows); I++)				\
			(ptr)[I] = malloc((cols) * sizeof (type));	\
	}								\
}

Then you can just do:

	double **mat;

	matrix(double, mat, 10, 10);

	mat[5][5] = 1.0;	/* no problem! */

The macro should have more robust ``rollback'', since any of the mallocs can
fail, in which case you end up with a partial block of valid pointers.

Is there any reason in your program why you can't simply allocate rows *
columns data items with a single malloc invocation?
-- 
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads