Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > int** to void**

Reply
Thread Tools

int** to void**

 
 
Ben Bacarisse
Guest
Posts: n/a
 
      01-06-2010
Donkey Hottie <(E-Mail Removed)> writes:

> On 6.1.2010 6:03, Keith Thompson wrote:
>> Seebs<(E-Mail Removed)> writes:
>>> On 2010-01-06, John<(E-Mail Removed)> wrote:
>>>> So anyone can explain it for me and help me avoid this warning?
>>>
>>> Quite simply, you can't do that.
>>>
>>> You can convert void* to int*, but not void** to int**.

>>
>> Correction: You *can* convert from void** to int** (or, in this case,
>> from int** to void**) by using a cast, but you almost certainly
>> shouldn't.
>>

>
> Why not? If pointer arithmetic is not wanted, what do we miss? A
> pointer is pointer no matter what.


Keith's given you one example, but let me give you one from a program
I once had to port to a word-addressed machine. On this machine, all
"natural" pointers pointed to two-byte words. To get char * and void
* types, the compiler doubled the pointer and used the bottom bit to
say which byte to point to (actually this was done in the microcode of
the CPU so it was faster then it sounds). The effect was that a cast
from void * to int * shifted the value right by one. If you took data
that was a genuine void ** and simply forced it to be considered int
** there is no way that the cast can do change all those void *s that
are pointed to and halve them all! The code was nightmare to port.

Of course word addressed machine are largely dead now, and the signs
are that they won't come back. but who can say for sure?

--
Ben.
 
Reply With Quote
 
 
 
 
Keith Thompson
Guest
Posts: n/a
 
      01-06-2010
John <(E-Mail Removed)> writes:
> On Jan 6, 4:42*pm, Keith Thompson <(E-Mail Removed)> wrote:

[...]
>> As I said, using memset to initialize the index is superfluous, since
>> you immediately assign values to every element of the index anyway,
>> overwriting all the zeros. *As for the table, it can hold any
>> arbitrary type. *For integer types, memset will set the elements to zero.
>> For other types, it may or may not.
>>
>> You might consider just leaving the table uninitialized. *Typically
>> the code that calls ORCdarraynew is going to assign values to it
>> anyway.
>>
>> Or you can use memset to zero it, but keep in mind that, though this
>> will give you consisten results on a given system, those results
>> aren't necessarily meaningful.

>
> I think the concern here is assign integer 0 to pointer or double in C
> to
> init, Is it wrong?


The memset call doesn't exactly assign the integer 0. It sets the
representation to all-bits-zero (by assigning 0 to the elements of an
array of unsigned char overlaid on your data). For integer types,
that's guaranteed to give you 0 (though that guarantee wasn't stated
in the standard until one of the post-C99 Technical Corrigenda). For
pointer and floating-point types, there is no such guarantee.

[...]

>> > I think double* and double** and void* and void** have same length.
>> > then what is your concern?

>>
>> Why do you think they have the same length? *They very likely do on
>> your system, but that's a non-portable assumption.
>>
>> Now you're free to write non-portable code if you want to. But
>> personally, I find that portable code tends to be cleaner, even if
>> it's never actually going to be ported.

>
> I think in any platform, the pointer have the same length, do you
> think so?


What? No. It's been explained several times that different pointer
types do *not* necessarily have the same size or representation.
They're the same on most modern systems, but who knows what odd
system your code might be ported to in the future?

[...]

--
Keith Thompson (The_Other_Keith) http://www.velocityreviews.com/forums/(E-Mail Removed) <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
 
 
 
Nobody
Guest
Posts: n/a
 
      01-06-2010
On Tue, 05 Jan 2010 22:23:27 -0800, Keith Thompson wrote:

>>> We can call them like:
>>>
>>> int **p;
>>> p = (int**)ORCdarraynew(nrows, ncols, sizeof(int));
>>>
>>> ORCdarrayfree(p);

> [...]
>> Why don't you rewrite ORCdarraynew to return type (int**) instead?

>
> Because it's intended to allocate a 2d array of any arbitrary type;
> that's what the "size" parameter is for.


Fair enough. In which case, don't try to cast the result.

If you have a function which expects an int**, change it to accept a
void** instead, then cast the individual pointers, e.g. change:

p[i][j]

to:
((int *)p[i])[j]

This is safe even if int* and void* have different representations, as the
cast will perform any necessary conversions.


 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      01-06-2010
Nobody <(E-Mail Removed)> writes:

> On Tue, 05 Jan 2010 22:23:27 -0800, Keith Thompson wrote:
>
>>>> We can call them like:
>>>>
>>>> int **p;
>>>> p = (int**)ORCdarraynew(nrows, ncols, sizeof(int));
>>>>
>>>> ORCdarrayfree(p);

>> [...]
>>> Why don't you rewrite ORCdarraynew to return type (int**) instead?

>>
>> Because it's intended to allocate a 2d array of any arbitrary type;
>> that's what the "size" parameter is for.

>
> Fair enough. In which case, don't try to cast the result.
>
> If you have a function which expects an int**, change it to accept a
> void** instead, then cast the individual pointers, e.g. change:
>
> p[i][j]
>
> to:
> ((int *)p[i])[j]
>
> This is safe even if int* and void* have different representations, as the
> cast will perform any necessary conversions.


That works for int * but I suspect the OP is passing round void **s in
an attempt to be type-independent. The original post said that the
compiler complained about passing void ** to an int ** parameter. I
don't think there is any safe and portable way to do that. I suspect
the OP needs to (a) check for alignment issues already discussed and
(b) keep the pointer as a void ** until the moment the element is
accessed.

--
Ben.
 
Reply With Quote
 
Seebs
Guest
Posts: n/a
 
      01-06-2010
On 2010-01-06, John <(E-Mail Removed)> wrote:
> The reason you don't agree with memset is memset set the elements to
> integer?


No. It sets the raw underlying bit patterns byte by byte, and that's not
guaranteed to be meaningful in non-integer types.

>> * * * * int *rows;
>> * * * * int *data;
>> * * * * data = malloc(rows * columns * sizeof(int));
>> * * * * rows = malloc(rows * sizeof(int *);


> 2 malloc functions nearby means the space is continus?


No.

There is no reason for the table of indexes and the table of rows to be
contiguous. ("Contiguous" -- having a shared boundary. Probably a better
word than "continuous" here.)

> You code don't solve the multiple data type issue either, I think.


You're right, it doesn't. You can't solve that completely generically
in C, because pointers to different types are not necessarily interchangeable.

You can come surprisingly close with a macro, though.

#define MAKE_2D_ARRAY(ptr, r, c, t) do { \
t *data; \
t **rows; \
/* fill this part in with the logic from the previous program */ \
ptr = rows[0]; \
} while(0);

#define FREE_2D_ARRAY(r) do { \
if (r) { free(r[0]); } free r;
} while(0);

(The "do... while(0)" idiom is something you can look up in the FAQ.)

-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / (E-Mail Removed)
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
 
Reply With Quote
 
lawrence.jones@siemens.com
Guest
Posts: n/a
 
      01-06-2010
Ben Bacarisse <(E-Mail Removed)> wrote:
>
> Of course word addressed machine are largely dead now, and the signs
> are that they won't come back. but who can say for sure?


Exactly. We thought the same thing about decimal floating point not so
long ago, and now it's the hot "new" thing!
--
Larry Jones

You just can't ever be too careful. -- Calvin
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      01-06-2010
pete <(E-Mail Removed)> writes:

> Ben Bacarisse wrote:
>> Nobody <(E-Mail Removed)> writes:
>>
>>
>>>On Tue, 05 Jan 2010 22:23:27 -0800, Keith Thompson wrote:
>>>
>>>
>>>>>>We can call them like:
>>>>>>
>>>>>>int **p;
>>>>>>p = (int**)ORCdarraynew(nrows, ncols, sizeof(int));
>>>>>>
>>>>>>ORCdarrayfree(p);
>>>>
>>>>[...]
>>>>
>>>>>Why don't you rewrite ORCdarraynew to return type (int**) instead?
>>>>
>>>>Because it's intended to allocate a 2d array of any arbitrary type;
>>>>that's what the "size" parameter is for.
>>>
>>>Fair enough. In which case, don't try to cast the result.
>>>
>>>If you have a function which expects an int**, change it to accept a
>>>void** instead, then cast the individual pointers, e.g. change:
>>>
>>> p[i][j]
>>>
>>>to:
>>> ((int *)p[i])[j]
>>>
>>>This is safe even if int* and void* have different representations, as the
>>>cast will perform any necessary conversions.

>>
>>
>> That works for int * but I suspect the OP is passing round void **s in
>> an attempt to be type-independent. The original post said that the
>> compiler complained about passing void ** to an int ** parameter. I
>> don't think there is any safe and portable way to do that. I suspect
>> the OP needs to (a) check for alignment issues already discussed and
>> (b) keep the pointer as a void ** until the moment the element is
>> accessed.
>>

>
> I think it works out best to pass a (void *)
> and to use a (void **) internally.


I don't see how your code copes with the portability issues that have
been brought up. If, say, arithmetic is required to convert a void *
to an int * and/or they are of different sizes) how can your example
help?

Maybe you are not suggesting a portable solution but one that works
when all pointers have the same size and representation? If so, sure,
I'd return a void * too and try to put in a compile-time assert for
the things I am assuming about the pointers.

<snip code>
--
Ben.
 
Reply With Quote
 
John
Guest
Posts: n/a
 
      01-07-2010
On Jan 7, 3:24*am, Seebs <(E-Mail Removed)> wrote:
> On 2010-01-06, John <(E-Mail Removed)> wrote:
>
> > The reason you don't agree with memset is memset set the elements to
> > integer?

>
> No. *It sets the raw underlying bit patterns byte by byte, and that's not
> guaranteed to be meaningful in non-integer types.
>
> >> int *rows;
> >> int *data;
> >> data = malloc(rows * columns * sizeof(int));
> >> rows = malloc(rows * sizeof(int *);

> > 2 malloc functions nearby means the space is continus?

>
> No.
>
> There is no reason for the table of indexes and the table of rows to be
> contiguous. *("Contiguous" -- having a shared boundary. *Probably a better
> word than "continuous" here.)
>
> > You code don't solve the multiple data type issue either, I think.

>
> You're right, it doesn't. *You can't solve that completely generically
> in C, because pointers to different types are not necessarily interchangeable.
>
> You can come surprisingly close with a macro, though.
>
> #define MAKE_2D_ARRAY(ptr, r, c, t) do { \
> * t *data; \
> * t **rows; \
> * /* fill this part in with the logic from the previous program */ \
> * ptr = rows[0]; \
>
> } while(0);
>
> #define FREE_2D_ARRAY(r) do { \
> * if (r) { free(r[0]); } free r;
>
> } while(0);
>
> (The "do... while(0)" idiom is something you can look up in the FAQ.)
>


Use macro is a good way indeed. thanks!

 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      01-07-2010
pete <(E-Mail Removed)> writes:

> Ben Bacarisse wrote:

<snip>
>> Maybe you are not suggesting a portable solution but one that works
>> when all pointers have the same size and representation?

>
> Yes.
> That's as close as I got to what OP wanted.
>
> unsigned **p;
> double **d;
> char ***c;
>
> assert(sizeof *c == sizeof **c
> && sizeof *p == sizeof **c
> && sizeof *d == sizeof **c);


For safety one would want to check the representation. I doubt that
can be done 100% reliably. Using memcpy would be clumsy, but it might
be worth it.

>> If so, sure,
>> I'd return a void * too and try to put in a compile-time assert for
>> the things I am assuming about the pointers.


I can't think of a compile-time representation test so that notion was
pie in the sky form me.

--
Ben.
 
Reply With Quote
 
John
Guest
Posts: n/a
 
      01-07-2010
On Jan 7, 2:40*am, pete <(E-Mail Removed)> wrote:
> Ben Bacarisse wrote:
> > Nobody <(E-Mail Removed)> writes:

>
> >>On Tue, 05 Jan 2010 22:23:27 -0800, Keith Thompson wrote:

>
> >>>>>We can call them like:

>
> >>>>>int **p;
> >>>>>p = (int**)ORCdarraynew(nrows, ncols, sizeof(int));

>
> >>>>>ORCdarrayfree(p);

>
> >>>[...]

>
> >>>>Why don't you rewrite ORCdarraynew to return type (int**) instead?

>
> >>>Because it's intended to allocate a 2d array of any arbitrary type;
> >>>that's what the "size" parameter is for.

>
> >>Fair enough. In which case, don't try to cast the result.

>
> >>If you have a function which expects an int**, change it to accept a
> >>void** instead, then cast the individual pointers, e.g. change:

>
> >> * * * *p[i][j]

>
> >>to:
> >> * * * *((int *)p[i])[j]

>
> >>This is safe even if int* and void* have different representations, as the
> >>cast will perform any necessary conversions.

>
> > That works for int * but I suspect the OP is passing round void **s in
> > an attempt to be type-independent. *The original post said that the
> > compiler complained about passing void ** to an int ** parameter. *I
> > don't think there is any safe and portable way to do that. *I suspect
> > the OP needs to (a) check for alignment issues already discussed and
> > (b) keep the pointer as a void ** until the moment the element is
> > accessed.

>
> I think it works out best to pass a (void *)
> and to use a (void **) internally.
>
> /* BEGIN orc.c output */
>
> * 0 *1 *2 *3 *4 *5 *6
> * 1 *2 *3 *4 *5 *6 *7
> * 2 *3 *4 *5 *6 *7 *8
> * 3 *4 *5 *6 *7 *8 *9
> * 4 *5 *6 *7 *8 *9 10
>
> * *zero * *one * *two *three * four * five * *six
> * * one * *two *three * four * five * *six *seven
> * * two *three * four * five * *six *seven *eight
> * three * four * five * *six *seven *eight * nine
> * *four * five * *six *seven *eight * nine * *ten
>
> * 0.000000 *1.000000 *2.000000 *3.000000 *4.000000 *5.000000 *6.000000
> * 1.000000 *2.000000 *3.000000 *4.000000 *5.000000 *6.000000 *7.000000
> * 2.000000 *3.000000 *4.000000 *5.000000 *6.000000 *7.000000 *8.000000
> * 3.000000 *4.000000 *5.000000 *6.000000 *7.000000 *8.000000 *9.000000
> * 4.000000 *5.000000 *6.000000 *7.000000 *8.000000 *9.000000 10.000000
>
> /* END orc.c output */
>
> /* BEGIN orc.c */
>
> #include <stdlib.h>
> #include <string.h>
> #include <stdio.h>
>
> #define STRINGS * * * * * * * * * * * * * * \
> {"zero","one","two","three","four","five", *\
> * "six","seven","eight","nine","ten"}


Here STRINGS was not used in main function.
and could you please tell me the reason you
use macro here? why not string array in main
function?

>
> void
> ORCdarrayfree(void *arr, size_t nmemb);


The name nmemb's meaning is?

> void*
> ORCdarraynew(size_t row, size_t col, size_t size);
>
> int main(void)
> {
> * * *char *string[] = STRINGS;
> * * *int nrows = 5;
> * * *int ncols = 7;
> * * *int a, b;
> * * *int **p;
> * * *double **d;
> * * *char ***c;
>
> * * *puts("/* BEGIN orc.c output */\n");
> * * *p = ORCdarraynew(nrows, ncols, sizeof **p);
> * * *if (p != NULL) {
> * * * * *for (a = 0; a != nrows; ++a) {
> * * * * * * *for (b = 0; b != ncols; ++b) {
> * * * * * * * * *p[a][b] = a + b;
> * * * * * * *}
> * * * * *}
> * * * * *for (a = 0; a != nrows; ++a) {
> * * * * * * *for (b = 0; b != ncols; ++b) {
> * * * * * * * * *printf("%2d ",p[a][b]);
> * * * * * * *}
> * * * * * * *putchar('\n');
> * * * * *}
> * * * * *ORCdarrayfree(p, nrows);
> * * *}
> * * *putchar('\n');
> * * *c = ORCdarraynew(nrows, ncols, sizeof **c);
> * * *if (c != NULL) {
> * * * * *for (a = 0; a != nrows; ++a) {
> * * * * * * *for (b = 0; b != ncols; ++b) {
> * * * * * * * * *c[a][b] = string[a + b];
> * * * * * * *}
> * * * * *}
> * * * * *for (a = 0; a != nrows; ++a) {
> * * * * * * *for (b = 0; b != ncols; ++b) {
> * * * * * * * * *printf("%6s ",c[a][b]);
> * * * * * * *}
> * * * * * * *putchar('\n');
> * * * * *}
> * * * * *ORCdarrayfree(c, nrows);
> * * *}
> * * *putchar('\n');
> * * *d = ORCdarraynew(nrows, ncols, sizeof **d);
> * * *if (d != NULL) {
> * * * * *for (a = 0; a != nrows; ++a) {
> * * * * * * *for (b = 0; b != ncols; ++b) {
> * * * * * * * * *d[a][b] = a + b;
> * * * * * * *}
> * * * * *}
> * * * * *for (a = 0; a != nrows; ++a) {
> * * * * * * *for (b = 0; b != ncols; ++b) {
> * * * * * * * * *printf("%9f ", d[a][b]);
> * * * * * * *}
> * * * * * * *putchar('\n');
> * * * * *}
> * * * * *ORCdarrayfree(d, nrows);
> * * *}
> * * *putchar('\n');
> * * *puts("/* END orc.c output */");
> * * *return 0;
>
> }
>
> void
> ORCdarrayfree(void *arr, size_t nmemb)
> {
> * * *void **const base = arr;


Why here need const? what is it mean?

>
> * * *while (nmemb-- != 0) {
> * * * * *free(base[nmemb]);
> * * *}
> * * *free(arr);
>
> }
>
> void*
> ORCdarraynew(size_t row, size_t col, size_t size)
> {
> * * void **arr;
> * * size_t index;
>
> * * arr = malloc(row * sizeof *arr);


Use sizeof like above workable?

> * * if (arr != NULL) {
>
> * * * *for (index = 0; index != row; ++index) {
> * * * * * arr[index] = malloc(col * size);
> * * * * * if (arr[index] == NULL) {
> * * * * * * * ORCdarrayfree(arr, index);
> * * * * * * * arr = NULL;
> * * * * * * * break;
> * * * * * }


Free allocated memory is good when malloc failed.

> * * * *}
> * * }
> * * return arr;
>
> }
>
> /* END orc.c */




John Cui
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off




Advertisments