int** to void**

B

Ben Bacarisse

Donkey Hottie said:
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?
 
K

Keith Thompson

John said:
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 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?

[...]
 
N

Nobody

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[j]

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

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

Ben Bacarisse

Nobody said:
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[j]

to:
((int *)p)[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.
 
S

Seebs

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.
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
 
L

lawrence.jones

Ben Bacarisse said:
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!
 
B

Ben Bacarisse

pete said:
Ben said:
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[j]

to:
((int *)p)[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>
 
J

John

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.
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!
 
B

Ben Bacarisse

pete said:
Ben Bacarisse wrote:

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.

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

John

Ben said:
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[j]
to:
       ((int *)p)[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] = a + b;
             }
         }
         for (a = 0; a != nrows; ++a) {
             for (b = 0; b != ncols; ++b) {
                 printf("%2d ",p[a]);
             }
             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] = string[a + b];
             }
         }
         for (a = 0; a != nrows; ++a) {
             for (b = 0; b != ncols; ++b) {
                 printf("%6s ",c[a]);
             }
             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] = a + b;
             }
         }
         for (a = 0; a != nrows; ++a) {
             for (b = 0; b != ncols; ++b) {
                 printf("%9f ", d[a]);
             }
             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
 
J

John

Ben said:
Ben Bacarisse wrote:
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[j]
to:
       ((int *)p)[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?

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);
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 run the above assert program on my pc, it works, which mean the
sizeof all
pointers are same. At least on my pc.

John Cui
 
M

Michael Foukarakis

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

Do not confuse the man. From the standard, "An integer constant
expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant. If a null pointer constant
is converted to a
pointer type, the resulting pointer, called a null pointer, is
guaranteed to compare unequal
to a pointer to any object or function." and therefore memset() can be
safely used to produce null pointers since, by definition, it copies
the value of its second operand into each of the n characters of the
object pointed to by its first operand (see 7.12.6.1 in ISO/IEC
9899:TC2).

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

That's also wrong. See 6.3.2.3 in the standard. You don't need
interchangeable pointers to solve the multiple data type issue, you
need the ability to convert to void* and back. Which is guaranteed.
 
M

Michael Foukarakis

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.
Do you have an example of an architecture that has different
representations for different pointers? I haven't seen one for as long
as I can recall, I'd be really interested in an example. (no irony, I
really would be!)
 
K

Keith Thompson

Michael Foukarakis said:
Do not confuse the man. From the standard, "An integer constant
expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant. If a null pointer constant
is converted to a
pointer type, the resulting pointer, called a null pointer, is
guaranteed to compare unequal
to a pointer to any object or function." and therefore memset() can be
safely used to produce null pointers since, by definition, it copies
the value of its second operand into each of the n characters of the
object pointed to by its first operand (see 7.12.6.1 in ISO/IEC
9899:TC2).

You are mistaken.

A null pointer may be obtained by *converting* a null pointer constant
to a pointer type. There is no implication that the conversion simply
copies the representation, any more than converting from int to float
copies the representation. Conversion constructs a value.

memset() copies the value of its second parameter to each byte of the
target object. So it sets the target to all-bits-zero -- which may or
may not be the representation of a null pointer value.
That's also wrong. See 6.3.2.3 in the standard. You don't need
interchangeable pointers to solve the multiple data type issue, you
need the ability to convert to void* and back. Which is guaranteed.

Converting a pointer to void* and back is not the same thing as
interpreting a pointer object as if it were of type void*.

Read section 5 of the comp.lang.c FAQ, <http://www.c-faq.com/>.
 
K

Keith Thompson

Michael Foukarakis said:
Do you have an example of an architecture that has different
representations for different pointers? I haven't seen one for as long
as I can recall, I'd be really interested in an example. (no irony, I
really would be!)

Old x86 systems had multiple memory models and "near" and "far"
pointers that were of different sizes.

I worked on a Cray T90 system where machine addresses pointed to
64-bit words, but the C compiler used 8-bit bytes. A char* pointer
stored a byte offset (0-7) in the high-order 3 bits of a word pointer.
(This worked because the system didn't support the full 64-bit address
space.)

I think there have been other systems with similar characterics where
there weren't any extra bits available, so the offset was stored in a
second "word", so a char* is actually bigger than an int*.
 
M

Michael Foukarakis

Do not confuse him further.

I wasn't addressing the OP. :)
 > From the standard, "An integer constant> expression with the value 0, or such an expression cast to type

 > pointer, is guaranteed to compare unequal to a pointer to any object
 > or function."

True enough.


 > null pointers

Not the case.

 > since, by definition, it copies the value of its
 > second operand into each of the n characters of the


The second parameter (not operand) to memset has int type, not pointer type.

Parameter, right. Damned caffeine withdrawal. :/
There are implementations with null pointer representations that are not
all-bits-zero. Let's take a hypothetical example: a C8S16ILP32
implementation - hardly uncommon so far - but with null pointers being
represented by all-bits-except one being zero, with that top bit being
set for some reason. Let's look at a memset call:

int *p;
memset(&p, 0, sizeof p);

memset has no idea what type &p points to, and it doesn't need to know -
it deals in raw bytes. This code fragment will set all the 32 bits of p
to 0, without regard for your intent that it should create a null
pointer. On the implementation described, it would not create a null
pointer.

<snip>

Reply to both RH and KT:

You are right, in the general case I am wrong, and there are indeed
architectures (largely obsolete, I'd argue, at least for the OP) where
using memset() would not produce null pointers. I guess that's what I
get for growing up with x86. :)

So, OP, if you want to construct null pointers, don't mess with their
internal representation (which you don't have to know anyways) and
your code may some day be able to run on a CDC Cyber 180, a pioneer
back in its day (the not-so-distant '70s), made by Canadians (now you
know who's to blame, too!).
 
M

Michael Foukarakis

Old x86 systems had multiple memory models and "near" and "far"
pointers that were of different sizes.

Sizes are irrelevant, aren't they?
I worked on a Cray T90 system where machine addresses pointed to
64-bit words, but the C compiler used 8-bit bytes.  A char* pointer
stored a byte offset (0-7) in the high-order 3 bits of a word pointer.
(This worked because the system didn't support the full 64-bit address
space.)

This *is* relevant. ;)
Converting a pointer to void* and back is not the same thing as
interpreting a pointer object as if it were of type void*.

How is that relevant to my point? I agree with you, I just don't see
how it affects the multiple data type issue, unless you're trying to
make the point that confusing the two would lead to a logical error,
which I still agree with but it's not limited to the multiple data
type and ADT implementation areas.
 
M

Michael Foukarakis

Michael Foukarakis wrote:




Whilst you are probably right that there are currently no popular
mainstream machines that have non-allbitszero null pointers, it is
perhaps unwise to assume that things will stay that way. Engineers have
a way of coming up with old ways to solve new problems (as well as new
ways of solving old problems!), and who is to say that some as yet
unknown hardware manufacturer won't use non-allbitszero null pointers as
an integral part of their revolutionary, astoundingly fast, amazingly
small, totally Intel-busting TheLastComputerYouWillEverNeed(tm) device?


And some day it might even run on the TheLastComputerYouWillEverNeed,
too. And possibly even the TheLastComputerYouWillEverNeed 2.

Heh, I'd like to live to see that. :)
 
D

David Thompson

void **arr;

arr = (void **) malloc (sizeof(void *) * row + size * row * col);

Casting the result of malloc is unnecessary and can mask errors.
if ( arr != NULL ) {
void *head;

head = (void *) arr + sizeof(void *) * row;
memset (arr, 0, sizeof(void *) * row + size * row * col);
while (row--) {
arr[row] = head + size * row * col;
}
}

return arr;
} /* End of ORCdarraynew*/

So you're allocating a chunk of memory consisting of an array of void*
pointers, immediately followed by a 2-dimensional array of some
arbitrary data, where each of the initial void* pointers points to a
row in the 2d array. Interesting. Let's call these two sections the
"index" and the "table", respectively.

You're making some non-portable assumptions, though.

The memset call zeros both the index and the table. The former is
useless, since you immediately initialize the table anyway. Zeroing

Typo: s/table/index/
the table may or may not be sensiple, depending on what data you're
storing. Remember that null pointers aren't necessarily all-bits-zero
(though they commonly are).
Plus he's (twice) using void* + integer assuming stride 1. GCC does
this as an extension, but it's not standard. Trivial to fix though.
You're also assuming that the table will be properly aligned.
Consider a system where void* is 32-bit aligned, double is 64-bit
aligned, and you want to allocate a table of doubles with an odd
number of rows.
You can easily enough make the index reservation be rows * sizeofptr
_rounded up to a multiple of (elem)size_. It may waste a little space
sometimes, but probably not more than a second malloc. Unless you have
really big elements, and then you could add a parameter to specify
alignment and perhaps wrap the calls in a macro which supplies
offsetof( struct{char force; T align;} , align ) or equivalent.

You're still stuck with the fact that the void* index elements are not
officially valid to access otherT[][] . But in today's world this does
work, and I for one would accept this with a suitable LOUD comment,
or better an autoconf or startup check something like:
int dummy, *p = &dummy; void * q = p; // note safe order
assert (sizeof p == sizeof q && memcmp (&p,&q,sizeof p)==0
&& "FATAL pointers don't have identical representations"
&& "Call <author> 999-555-1212 immediately day or night" );

But I'd rather just use VLA/VMTs. That's what they're there for.

<snip rest>
 
D

David Thompson

Old x86 systems had multiple memory models and "near" and "far"
pointers that were of different sizes.
Current/recent x86-32 still have them, but they're only needed for
visible segments (which few people seem to want now), or more than 4GB
(rather clunkily) without going to x86-64, a backwater we seem to have
passed by much more quickly than the years of 16-32bit torment.
I worked on a Cray T90 system where machine addresses pointed to
64-bit words, but the C compiler used 8-bit bytes. A char* pointer
stored a byte offset (0-7) in the high-order 3 bits of a word pointer.
(This worked because the system didn't support the full 64-bit address
space.)
The DEC PDP-10 had 36bit word, and nominal 18bit address space,
so a pointer to word or bigger could fit (and was often put) in a
halfword -- although to use the (often convenient) indirect and
indexed address modes it had to be stored in a fullword. Whereas a
pointer to subword used a hardware-defined 18+10+5bit 'byte pointer'
format stored in a fullword. Which were (deliberately) compatible in
that a byteptr could be used as a wordptr to the word containing the
byte (the unnecessary byte-selector being ignored).

The Tandem NonStop (retronymed NonStop 1 or TNS1 and AFAIK still
emulated in successor systems now from HP) has 16bit word and 64KW
address space, and uses a 16bit pointer to word or bigger, but a
15+1bit pointer to a byte within a word limited to the lower 32KW.
In this case indirecting through a wordptr as if it were a byteptr or
vice versa causes chaos. TNS was described as being like HP 3000 in
this and some other (ISA) respects, and I heard that DG Nova did much
the same thing, but I don't have personal experience of either.

On a related issue, both TNS1 and successor TNS2 (with 32bit byte
pointers for all data) have a completely different format for pointers
_to functions_, which are in separate spaces often not addressable by
data pointers at all. On this architecture nonstandard tricks like
sizeoffunc = &nextfunc - &thisfunc;
or
int main [] = { 05000, 0207 };
aren't even in the same county as working.
 

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,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top