Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

int** to void**

2 views
Skip to first unread message

John

unread,
Jan 5, 2010, 10:14:43 PM1/5/10
to
Hi All,

I wrote a function in C and compiled in gcc, I got a warning such as:

../test/test.c:26: warning: passing arg 1 of `ORCdarrayfree' from
incompatib
le pointer type

Then I goto 26th line, I found the function ORCdarrayfree have a
parameter with type void**,
but I passed int** to it.

So anyone can explain it for me and help me avoid this warning?

Thanks!

Ralph Malph

unread,
Jan 5, 2010, 10:36:03 PM1/5/10
to
int** i;
void** v=(void**) i;

Seebs

unread,
Jan 5, 2010, 10:52:22 PM1/5/10
to
On 2010-01-06, John <cuiyou...@gmail.com> 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**.

-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet...@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!

Keith Thompson

unread,
Jan 5, 2010, 11:01:33 PM1/5/10
to
Ralph Malph <ralph...@altavista.com> writes:

> John wrote:
>> I wrote a function in C and compiled in gcc, I got a warning such as:
>>
>> ../test/test.c:26: warning: passing arg 1 of `ORCdarrayfree' from
>> incompatib
>> le pointer type
>>
>> Then I goto 26th line, I found the function ORCdarrayfree have a
>> parameter with type void**,
>> but I passed int** to it.
>>
>> So anyone can explain it for me and help me avoid this warning?
>
> int** i;
> void** v=(void**) i;

[top-posting corrected]

Using a cast to silence a warning is almost never the right thing
to do.

void* is a generic pointer type, in the sense that you can convert
from any pointer type (excluding pointer-to-function types) to void*
and back again without loss of information.

There is no generic pointer-to-pointer type.

I don't know what ORCdarrayfree, but apparently it requires an
argument of type void**, most likely pointing to the first element
of an array of void*. The int** argument you're passing is probably
a pointer to the first element of an array of int*. void* and int*
are distinct and incompatible types.

You need to pass a valid void** value to ORCdarrayfree. The question
is how you can obtain such a value. We can't guess at an answer
without knowing more about ORCdarrayfree and what you're trying to
pass to it.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <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"

Keith Thompson

unread,
Jan 5, 2010, 11:03:51 PM1/5/10
to
Seebs <usenet...@seebs.net> writes:
> On 2010-01-06, John <cuiyou...@gmail.com> 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.

Donkey Hottie

unread,
Jan 5, 2010, 11:16:54 PM1/5/10
to
On 6.1.2010 6:03, Keith Thompson wrote:
> Seebs<usenet...@seebs.net> writes:
>> On 2010-01-06, John<cuiyou...@gmail.com> 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.

--
http://www.iki.fi/jarif/

You will inherit millions of dollars.

Keith Thompson

unread,
Jan 5, 2010, 11:35:13 PM1/5/10
to
Donkey Hottie <don...@fred.pp.fi> writes:
> On 6.1.2010 6:03, Keith Thompson wrote:
>> Seebs<usenet...@seebs.net> writes:
>>> On 2010-01-06, John<cuiyou...@gmail.com> 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.

void* and int* are incompatible types.

Consider an implementation where void* is 64 bits and int* is 32
bits (such an implementation is certainly permitted, and there could
be valid reasons for it). Then the function expects a void** that
points to the first element of an array of void* objects, each of
which is 64 bits -- but you're passing it an int** that points to the
first element of an array of int* objects, each of which is 32 bits.

Conversions from one pointer type to another can be safe in certain
cases. Interpreting a stored object of one pointer type as if it
were of a different type is not safe.

And even if, as is the case on many implementations, all pointers
happen to have the same representation, treating different pointer
types as if they were interchangeable tends to be logically
incorrect, even if you can get away with it.

The ORCdarrayfree function that the OP is trying to call expects
a void** argument. Presumably there's a good reason for that.
Or perhaps ORCdarrayfree itself is poorly designed. We don't
have enough information to know what the right solution is,
but we can be reasonably sure that passing an int** to something
expecting a void** isn't it.

John

unread,
Jan 5, 2010, 11:39:33 PM1/5/10
to
On Jan 6, 12:01 pm, Keith Thompson <ks...@mib.org> wrote:

ORCdarrayfree is used to free 2-d array allocated by malloc.
The src is:

/* ORCdarraynew function return void** ,
and ORCdarrayfree should get void** to
make these 2 functions to be used in
allocating integer, char, and other
customize type */

void**
ORCdarraynew (int row, int col, int size)
{
void **arr;

arr = (void **) malloc (sizeof(void *) * row + size * row * col);
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*/

void
ORCdarrayfree (void **arr)
{
if ( arr != NULL ) {
free (arr);
arr = NULL;
}
} /* End of ORCdarrayfree*/


We can call them like:

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

ORCdarrayfree(p);

So the parameter of ORCdarrayfree will be converted from int** to
void**.
While, the gcc compiler give me a warning:

warning: passing arg 1 of `ORCdarrayfree' from
incompatib
le pointer type

So please help me.


Keith Thompson

unread,
Jan 6, 2010, 12:10:16 AM1/6/10
to
John <cuiyou...@gmail.com> writes:
> On Jan 6, 12:01 pm, Keith Thompson <ks...@mib.org> wrote:
[...]

>> You need to pass a valid void** value to ORCdarrayfree.  The question
>> is how you can obtain such a value.  We can't guess at an answer
>> without knowing more about ORCdarrayfree and what you're trying to
>> pass to it.
>>
>
> ORCdarrayfree is used to free 2-d array allocated by malloc.
> The src is:
>
> /* ORCdarraynew function return void** ,
> and ORCdarrayfree should get void** to
> make these 2 functions to be used in
> allocating integer, char, and other
> customize type */
>
> void**
> ORCdarraynew (int row, int col, int size)

These should probably be size_t rather than int.

> {
> 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
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).

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.

> void
> ORCdarrayfree (void **arr)
> {
> if ( arr != NULL ) {
> free (arr);
> arr = NULL;
> }
> } /* End of ORCdarrayfree*/

Note that the "arr != NULL" test is unnecessary; free() does nothing
if you give it a null pointer argument. And setting arr to NULL is
useless; you're just clobbering a local object. You might want to do
"*arr = NULL;" -- or you might just want to leave it alone.

>
> We can call them like:
>
> int **p;
> p = (int**)ORCdarraynew(nrows, ncols, sizeof(int));
>
> ORCdarrayfree(p);
>
> So the parameter of ORCdarrayfree will be converted from int** to
> void**.
> While, the gcc compiler give me a warning:
>
> warning: passing arg 1 of `ORCdarrayfree' from
> incompatib
> le pointer type

gcc is correct; the pointer types are incompatible.

> So please help me.

I think you need to allocate the index and the table in two separate
calls to malloc().

Here's a thought. Allocate row+1 void* elements for the index.
Allocate size*row*col bytes for the table; using a separate call to
malloc guarantees proper alignment. Store the address of the table in
the 0th element of the index, then return the address of the 1st
element. ORCdarrayfree can then index backwards to obtain the address
of the table, free the table, then free the index.

I haven't completely thought this through. This approach should be
type-safe, but I think you might have some problems with indexing
(e.g., p[x][y]).

pete

unread,
Jan 6, 2010, 1:13:04 AM1/6/10
to

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

--
pete

Keith Thompson

unread,
Jan 6, 2010, 1:23:27 AM1/6/10
to
pete <pfi...@mindspring.com> writes:
> John wrote:
[...]

>> /* ORCdarraynew function return void** ,
>> and ORCdarrayfree should get void** to
>> make these 2 functions to be used in
>> allocating integer, char, and other
>> customize type */
>>
>> void**
>> ORCdarraynew (int row, int col, int size)
>> {
[...]

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

pete

unread,
Jan 6, 2010, 2:11:45 AM1/6/10
to
Keith Thompson wrote:
> pete <pfi...@mindspring.com> writes:
>
>>John wrote:
>
> [...]
>
>>>/* ORCdarraynew function return void** ,
>>> and ORCdarrayfree should get void** to
>>> make these 2 functions to be used in
>>> allocating integer, char, and other
>>> customize type */
>>>
>>>void**
>>>ORCdarraynew (int row, int col, int size)
>>>{
>
> [...]
>
>>>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.
>

OK, now I see; Thank you.


/* BEGIN new.c */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

void
ORCdarrayfree(void *arr, size_t nmemb);
void*
ORCdarraynew(size_t row, size_t col, size_t size);

int main(void)
{
int **p;
int nrows = 5;
int ncols = 7;
int a, b;
char **c;

p = ORCdarraynew(nrows, ncols, sizeof *p);
if (p != NULL) {
for (a = 0; a != nrows; ++a) {
for (b = 0; b != nrows; ++b) {
p[a][b] = a + b;
}
}
for (a = 0; a != nrows; ++a) {
for (b = 0; b != nrows; ++b) {
printf("%2d ",p[a][b]);
}
putchar('\n');
}
ORCdarrayfree(p, nrows);
}

c = ORCdarraynew(2, sizeof"two", 1);
if (c != NULL) {
strcpy(c[0], "one");
strcpy(c[1], "two");
puts(c[0]);
puts(c[1]);
ORCdarrayfree(c, 2);
}
return 0;
}

void
ORCdarrayfree(void *arr, size_t nmemb)
{
char **const base = arr;

while (nmemb-- != 0) {
free(base[nmemb]);
}
free (base);
}

void*
ORCdarraynew(size_t row, size_t col, size_t size)
{
char **arr;

arr = malloc(row * sizeof *arr);
if (arr != NULL) {
while (row-- != 0) {
arr[row] = malloc(col * size);
if (arr[row] == NULL) {
ORCdarrayfree(arr, row);
arr = NULL;
break;
}
}
}
return arr;
}

/* END new.c */


--
pete

pete

unread,
Jan 6, 2010, 2:18:16 AM1/6/10
to

The loop should count up, instead of down, because of
the way that ORCdarrayfree is called in case of malloc failure.

void*
ORCdarraynew(size_t row, size_t col, size_t size)
{
char **arr;

size_t index;

arr = malloc(row * sizeof *arr);
if (arr != NULL) {

for (index = 0; index != row; ++index) {


arr[row] = malloc(col * size);

if (arr[index] == NULL) {
ORCdarrayfree(arr, index);


arr = NULL;
break;
}
}
}
return arr;
}

--
pete

John

unread,
Jan 6, 2010, 2:21:39 AM1/6/10
to
On Jan 6, 1:10 pm, Keith Thompson <ks...@mib.org> wrote:

> John <cuiyouzhi0...@gmail.com> writes:
> > On Jan 6, 12:01 pm, Keith Thompson <ks...@mib.org> wrote:
> [...]
> >> You need to pass a valid void** value to ORCdarrayfree.  The question
> >> is how you can obtain such a value.  We can't guess at an answer
> >> without knowing more about ORCdarrayfree and what you're trying to
> >> pass to it.
>
> > ORCdarrayfree is used to free 2-d array allocated by malloc.
> > The src is:
>
> > /* ORCdarraynew function return void** ,
> >    and ORCdarrayfree should get void** to
> >    make these 2 functions to be used in
> >    allocating integer, char, and other
> >    customize type */
>
> > void**
> > ORCdarraynew (int row, int col, int size)
>
> These should probably be size_t rather than int.

Why?

>
> > {
> >    void **arr;
>
> >    arr = (void **) malloc (sizeof(void *) * row + size * row * col);
>
> Casting the result of malloc is unnecessary and can mask errors.

In C, I think so. Thanks.

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

memset is used to init the table and index, why is it wrong?

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

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

>
> > void
> > ORCdarrayfree (void **arr)
> > {
> >    if ( arr != NULL ) {
> >       free (arr);
> >       arr = NULL;
> >    }
> > } /* End of ORCdarrayfree*/
>
> Note that the "arr != NULL" test is unnecessary; free() does nothing
> if you give it a null pointer argument.  And setting arr to NULL is
> useless; you're just clobbering a local object.  You might want to do
> "*arr = NULL;" -- or you might just want to leave it alone.

free(arr) can free all space by malloc, but don't set arr to NULL.
I add set arr to NULL is to make the statement after ORCdarrayfree
can not use arr.

And also, I don't think I need to set *arr to NULL if I set arr to
NULL
after ORCnarrayfree.

>
>
>
> > We can call them like:
>
> > int **p;
> > p = (int**)ORCdarraynew(nrows, ncols, sizeof(int));
>
> > ORCdarrayfree(p);
>
> > So the parameter of ORCdarrayfree will be converted from int** to
> > void**.
> > While, the gcc compiler give me a warning:
>
> > warning: passing arg 1 of `ORCdarrayfree' from
> > incompatib
> > le pointer type
>
> gcc is correct; the pointer types are incompatible.
>
> > So please help me.
>
> I think you need to allocate the index and the table in two separate
> calls to malloc().
>
> Here's a thought.  Allocate row+1 void* elements for the index.
> Allocate size*row*col bytes for the table; using a separate call to
> malloc guarantees proper alignment.  Store the address of the table in
> the 0th element of the index, then return the address of the 1st
> element.  ORCdarrayfree can then index backwards to obtain the address
> of the table, free the table, then free the index.
>
> I haven't completely thought this through.  This approach should be
> type-safe, but I think you might have some problems with indexing
> (e.g., p[x][y]).
>

Bingo, cannot use p[x][y] is the reason why I don't use your way.


John

unread,
Jan 6, 2010, 2:26:00 AM1/6/10
to
On Jan 6, 3:18 pm, pete <pfil...@mindspring.com> wrote:
> pete wrote:
> > Keith Thompson wrote:
>

This kind new function can not allocate continues memory for 2-d
array, do you think so?

John Cui

John

unread,
Jan 6, 2010, 2:26:11 AM1/6/10
to
On Jan 6, 3:18 pm, pete <pfil...@mindspring.com> wrote:
> pete wrote:
> > Keith Thompson wrote:
>

This kind new function can not allocate continues memory for 2-d

Seebs

unread,
Jan 6, 2010, 2:25:34 AM1/6/10
to
On 2010-01-06, John <cuiyou...@gmail.com> wrote:
>> > ORCdarraynew (int row, int col, int size)
>>
>> These should probably be size_t rather than int.
>
> Why?

Because they're sizes, and sizes should usually be represented in
size_t. For instance, on some systems, you might find that size_t
can represent values up to 2 billion, while int can only handle values
up to 32,767.

> memset is used to init the table and index, why is it wrong?

Because using memset to "zero" a pointer is not guaranteed to produce
null pointers.

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

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

You missed the key case: Are "void *" and "double" (no *) the same length?
Not on some fairly common systems. What this means is that since you
start the doubles immediately after the index, if there's an odd number
of rows, you're starting to store 8-byte-aligned values on a 4-byte-aligned
address. That could fail.

> And also, I don't think I need to set *arr to NULL if I set arr to
> NULL
> after ORCnarrayfree.

Except that something else might still have a pointer to *arr, so you
want to make sure the thing pointed to has been zeroed out.

>> I haven't completely thought this through. �This approach should be
>> type-safe, but I think you might have some problems with indexing
>> (e.g., p[x][y]).

> Bingo, cannot use p[x][y] is the reason why I don't use your way.

There are ways to make that work which are more consistent with respecting
the spirit of C.

Here's the thing you're missing: It is better to write things according
to the formal specification of the language than according to what you think
you know about the specific machine you are using. It is more reliable,
and more likely to work even if you are wrong about the machine, or if the
machine gets changed suddenly.

There are programs out there that, on a few days' notice, had to convert
from 32-bit big-endian systems to 64-bit little-endian systems. If they were
written portably, that was no trouble at all...

pete

unread,
Jan 6, 2010, 2:36:10 AM1/6/10
to


No, but you can't do p[x][y] for arbitrary x and y,
with int **p;
unless p is pointing to a pointer.

--
pete

John

unread,
Jan 6, 2010, 2:46:04 AM1/6/10
to

arr = malloc(row * sizeof *arr); in your code
allocate a space,

arr[row] = malloc(col * size); in your code
allocate another space.

So I think these 2 spaces are not continues.

John Cui

John

unread,
Jan 6, 2010, 2:51:21 AM1/6/10
to
On Jan 6, 3:25 pm, Seebs <usenet-nos...@seebs.net> wrote:

> On 2010-01-06, John <cuiyouzhi0...@gmail.com> wrote:
>
> >> > ORCdarraynew (int row, int col, int size)
>
> >> These should probably be size_t rather than int.
>
> > Why?
>
> Because they're sizes, and sizes should usually be represented in
> size_t.  For instance, on some systems, you might find that size_t
> can represent values up to 2 billion, while int can only handle values
> up to 32,767.

Thanks!

>
> > memset is used to init the table and index, why is it wrong?
>
> Because using memset to "zero" a pointer is not guaranteed to produce
> null pointers.

OK, then how to init the index pointers and table elements in the 2-d
array?

>
> >> 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.
> > I think double* and double** and void* and void** have same length.
> > then what is your concern?
>
> You missed the key case:  Are "void *" and "double" (no *) the same length?
> Not on some fairly common systems.  What this means is that since you
> start the doubles immediately after the index, if there's an odd number
> of rows, you're starting to store 8-byte-aligned values on a 4-byte-aligned
> address.  That could fail.

OK, I used viod* for supporting multiple data types, then how to
handle both
multiple data types and align issue?

>
> > And also, I don't think I need to set *arr to NULL if I set arr to
> > NULL
> > after ORCnarrayfree.
>
> Except that something else might still have a pointer to *arr, so you
> want to make sure the thing pointed to has been zeroed out.

hmmm, you are more safer.

>
> >> I haven't completely thought this through.  This approach should be
> >> type-safe, but I think you might have some problems with indexing
> >> (e.g., p[x][y]).
> > Bingo, cannot use p[x][y] is the reason why I don't use your way.
>
> There are ways to make that work which are more consistent with respecting
> the spirit of C.
>
> Here's the thing you're missing:  It is better to write things according
> to the formal specification of the language than according to what you think
> you know about the specific machine you are using.  It is more reliable,
> and more likely to work even if you are wrong about the machine, or if the
> machine gets changed suddenly.

Anyway, the spirit of C is don't use p[][] kind?
and in my way, I can allocate continues memory for 2-d array. I think
it is better.

John Cui

Seebs

unread,
Jan 6, 2010, 3:09:44 AM1/6/10
to
On 2010-01-06, John <cuiyou...@gmail.com> wrote:
> OK, then how to init the index pointers and table elements in the 2-d
> array?

To zero pointers and floating point values:

for (i = 0; i < n; ++i)
a[i] = 0;

> OK, I used viod* for supporting multiple data types, then how to
> handle both
> multiple data types and align issue?

Usually, you do it by allocating two tables; one table for your pointers,
and another table for the data values they point to. If you do this right,
you can do p[x][y], and you can still free both values. (Because p[0]
will be the address of the whole data block you allocated, and p will
be the address of the index block.)

> Anyway, the spirit of C is don't use p[][] kind?

No.

> and in my way, I can allocate continues memory for 2-d array. I think
> it is better.

You still don't get it.

You can allocate continuous memory for a 2D array separately from the
table for it, and then p[x][y] works. (Only it's not really a 2D
array; it's a 1D array of pointers to data. But it works like a 2D array.)

int *rows;
int *data;
data = malloc(rows * columns * sizeof(int));
rows = malloc(rows * sizeof(int *);
for (i = 0; i < rows; ++i) {
rows[i] = data + (columns * i);
for (j = 0; 0 < columns; ++j)
rows[i][j] = 0;
}

Poof! rows[0][0] is the first integer in the data array, rows[3][4] is
the 5th item in the 4th row of the array, and so on. To free it:

free(rows[0]);
free(rows);

Two allocations, but the array contents are continuous.

John

unread,
Jan 6, 2010, 3:30:23 AM1/6/10
to
On Jan 6, 4:09 pm, Seebs <usenet-nos...@seebs.net> wrote:

> On 2010-01-06, John <cuiyouzhi0...@gmail.com> wrote:
>
> > OK, then how to init the index pointers and table elements in the 2-d
> > array?
>
> To zero pointers and floating point values:
>
>         for (i = 0; i < n; ++i)
>                 a[i] = 0;

The reason you don't agree with memset is memset set the elements to
integer?
++
memset
Syntax:
#include <string.h>
void *memset( void *buffer, int ch, size_t count );

The function memset() copies ch into the first count characters of
buffer, and returns buffer. memset() is useful for intializing a
section of memory to some value. For example, this command:
++

>
> > OK, I used viod* for supporting multiple data types, then how to
> > handle both
> > multiple data types and align issue?
>
> Usually, you do it by allocating two tables; one table for your pointers,
> and another table for the data values they point to.  If you do this right,
> you can do p[x][y], and you can still free both values.  (Because p[0]
> will be the address of the whole data block you allocated, and p will
> be the address of the index block.)
>
> > Anyway, the spirit of C is don't use p[][] kind?
>
> No.
>
> > and in my way, I can allocate continues memory for 2-d array. I think
> > it is better.
>
> You still don't get it.
>
> You can allocate continuous memory for a 2D array separately from the
> table for it, and then p[x][y] works.  (Only it's not really a 2D
> array; it's a 1D array of pointers to data.  But it works like a 2D array.)
>
>         int *rows;
>         int *data;
>         data = malloc(rows * columns * sizeof(int));
>         rows = malloc(rows * sizeof(int *);

2 malloc functions nearby means the space is continus?

>         for (i = 0; i < rows; ++i) {
>                  rows[i] = data + (columns * i);
>                  for (j = 0; 0 < columns; ++j)
>                         rows[i][j] = 0;
>         }
>
> Poof!  rows[0][0] is the first integer in the data array, rows[3][4] is
> the 5th item in the 4th row of the array, and so on.  To free it:
>
>         free(rows[0]);
>         free(rows);
>
> Two allocations, but the array contents are continuous.

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

John Cui

Keith Thompson

unread,
Jan 6, 2010, 3:42:27 AM1/6/10
to
John <cuiyou...@gmail.com> writes:
> On Jan 6, 1:10 pm, Keith Thompson <ks...@mib.org> wrote:
>> John <cuiyouzhi0...@gmail.com> writes:
>> > On Jan 6, 12:01 pm, Keith Thompson <ks...@mib.org> wrote:
>> [...]
>> >> You need to pass a valid void** value to ORCdarrayfree.  The question
>> >> is how you can obtain such a value.  We can't guess at an answer
>> >> without knowing more about ORCdarrayfree and what you're trying to
>> >> pass to it.
>>
>> > ORCdarrayfree is used to free 2-d array allocated by malloc.
>> > The src is:
>>
>> > /* ORCdarraynew function return void** ,
>> >    and ORCdarrayfree should get void** to
>> >    make these 2 functions to be used in
>> >    allocating integer, char, and other
>> >    customize type */
>>
>> > void**
>> > ORCdarraynew (int row, int col, int size)
>>
>> These should probably be size_t rather than int.
>
> Why?

Because size_t is the appropriate type to represent sizes. Type int
could be a short as 16 bits, even on a 32-bit system.

[...]

>> >    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
>> 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).
>
> memset is used to init the table and index, why is it wrong?

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.

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

>>
>> > void
>> > ORCdarrayfree (void **arr)
>> > {
>> >    if ( arr != NULL ) {
>> >       free (arr);
>> >       arr = NULL;
>> >    }
>> > } /* End of ORCdarrayfree*/
>>
>> Note that the "arr != NULL" test is unnecessary; free() does nothing
>> if you give it a null pointer argument.  And setting arr to NULL is
>> useless; you're just clobbering a local object.  You might want to do
>> "*arr = NULL;" -- or you might just want to leave it alone.
>
> free(arr) can free all space by malloc, but don't set arr to NULL.
> I add set arr to NULL is to make the statement after ORCdarrayfree
> can not use arr.

arr is a parameter to ORCdarrayfree. It's a local object that ceases
to exist immediately after you set it to NULL. It's a *copy* of the
argument passed by the caller. Modifying it has no effect on the
caller.

[...]

John

unread,
Jan 6, 2010, 10:28:33 AM1/6/10
to
On Jan 6, 4:42 pm, Keith Thompson <ks...@mib.org> wrote:
> John <cuiyouzhi0...@gmail.com> writes:
> > On Jan 6, 1:10 pm, Keith Thompson <ks...@mib.org> wrote:
> >> John <cuiyouzhi0...@gmail.com> writes:
> >> > On Jan 6, 12:01 pm, Keith Thompson <ks...@mib.org> wrote:
> >> [...]
> >> >> You need to pass a valid void** value to ORCdarrayfree.  The question
> >> >> is how you can obtain such a value.  We can't guess at an answer
> >> >> without knowing more about ORCdarrayfree and what you're trying to
> >> >> pass to it.
>
> >> > ORCdarrayfree is used to free 2-d array allocated by malloc.
> >> > The src is:
>
> >> > /* ORCdarraynew function return void** ,
> >> >    and ORCdarrayfree should get void** to
> >> >    make these 2 functions to be used in
> >> >    allocating integer, char, and other
> >> >    customize type */
>
> >> > void**
> >> > ORCdarraynew (int row, int col, int size)
>
> >> These should probably be size_t rather than int.
>
> > Why?
>
> Because size_t is the appropriate type to represent sizes.  Type int
> could be a short as 16 bits, even on a 32-bit system.

I agree now, thanks.

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

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

>
>
>
>
>
> >> > void
> >> > ORCdarrayfree (void **arr)
> >> > {
> >> >    if ( arr != NULL ) {
> >> >       free (arr);
> >> >       arr = NULL;
> >> >    }
> >> > } /* End of ORCdarrayfree*/
>
> >> Note that the "arr != NULL" test is unnecessary; free() does nothing
> >> if you give it a null pointer argument.  And setting arr to NULL is
> >> useless; you're just clobbering a local object.  You might want to do
> >> "*arr = NULL;" -- or you might just want to leave it alone.
>
> > free(arr) can free all space by malloc, but don't set arr to NULL.
> > I add set arr to NULL is to make the statement after ORCdarrayfree
> > can not use arr.
>
> arr is a parameter to ORCdarrayfree.  It's a local object that ceases
> to exist immediately after you set it to NULL.  It's a *copy* of the
> argument passed by the caller.  Modifying it has no effect on the
> caller.
>

Hmm, yes, arr is a local variable, just free(arr) is enough. thanks.


John Cui

Ben Bacarisse

unread,
Jan 6, 2010, 10:53:48 AM1/6/10
to
Donkey Hottie <don...@fred.pp.fi> writes:

> On 6.1.2010 6:03, Keith Thompson wrote:
>> Seebs<usenet...@seebs.net> writes:
>>> On 2010-01-06, John<cuiyou...@gmail.com> 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.

pete

unread,
Jan 6, 2010, 11:03:59 AM1/6/10
to

You are correct. They are not contiguous.

--
pete

Francis Glassborow

unread,
Jan 6, 2010, 11:33:18 AM1/6/10
to

But void** is not void* no more than char** is char*. The Standard makes
certain guarantees about void* and char* that are not made for char**
and void**. There is no requirement that the later be the same size and
layout as the former. void* can contain the value of any data pointer
(and that includes pointers to pointers) without loss of information
(other than of the type being pointed to). Note that a void* is not
required by the C Standard to be able to hold the value of a function
pointer though I believe that Posix does have such a requirement.

You can can convert from int** to void* and back again but there is no
requirement that you can convert from int** to void** and even if you
force the issue with a cast there is no requirement that information
will not have been lost.

Keith Thompson

unread,
Jan 6, 2010, 11:38:32 AM1/6/10
to
John <cuiyou...@gmail.com> writes:
> On Jan 6, 4:42 pm, Keith Thompson <ks...@mib.org> 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?

Nobody

unread,
Jan 6, 2010, 12:38:48 PM1/6/10
to
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.


Ben Bacarisse

unread,
Jan 6, 2010, 12:57:52 PM1/6/10
to
Nobody <nob...@nowhere.com> writes:

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.

pete

unread,
Jan 6, 2010, 1:40:34 PM1/6/10
to

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"}

void
ORCdarrayfree(void *arr, size_t nmemb);

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;

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);
if (arr != NULL) {

for (index = 0; index != row; ++index) {

arr[index] = malloc(col * size);


if (arr[index] == NULL) {
ORCdarrayfree(arr, index);
arr = NULL;
break;
}
}
}
return arr;
}

/* END orc.c */

--
pete

Seebs

unread,
Jan 6, 2010, 2:24:13 PM1/6/10
to
On 2010-01-06, John <cuiyou...@gmail.com> 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.)

lawrenc...@siemens.com

unread,
Jan 6, 2010, 4:20:05 PM1/6/10
to
Ben Bacarisse <ben.u...@bsb.me.uk> 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

Ben Bacarisse

unread,
Jan 6, 2010, 6:44:50 PM1/6/10
to
pete <pfi...@mindspring.com> writes:

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.

pete

unread,
Jan 6, 2010, 9:13:44 PM1/6/10
to

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.


--
pete

John

unread,
Jan 6, 2010, 9:17:00 PM1/6/10
to
On Jan 7, 3:24 am, Seebs <usenet-nos...@seebs.net> wrote:

Use macro is a good way indeed. thanks!

Ben Bacarisse

unread,
Jan 6, 2010, 9:23:22 PM1/6/10
to
pete <pfi...@mindspring.com> 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.

John

unread,
Jan 6, 2010, 9:23:42 PM1/6/10
to

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?

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

John

unread,
Jan 6, 2010, 9:29:10 PM1/6/10
to
On Jan 7, 10:13 am, pete <pfil...@mindspring.com> wrote:
> Ben Bacarisse wrote:

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


Michael Foukarakis

unread,
Jan 8, 2010, 2:57:19 AM1/8/10
to
On Jan 6, 9:24 pm, Seebs <usenet-nos...@seebs.net> wrote:

> On 2010-01-06, John <cuiyouzhi0...@gmail.com> 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.

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

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.

Michael Foukarakis

unread,
Jan 8, 2010, 2:59:21 AM1/8/10
to
On Jan 7, 1:44 am, Ben Bacarisse <ben.use...@bsb.me.uk> wrote:
> 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!)

Keith Thompson

unread,
Jan 8, 2010, 3:22:40 AM1/8/10
to
Michael Foukarakis <electr...@gmail.com> writes:
> On Jan 6, 9:24 pm, Seebs <usenet-nos...@seebs.net> wrote:
>> On 2010-01-06, John <cuiyouzhi0...@gmail.com> 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.
>
> 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.

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

Keith Thompson

unread,
Jan 8, 2010, 3:25:45 AM1/8/10
to

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

Richard Heathfield

unread,
Jan 8, 2010, 3:31:41 AM1/8/10
to
Michael Foukarakis wrote:
> On Jan 6, 9:24 pm, Seebs <usenet-nos...@seebs.net> wrote:
>> On 2010-01-06, John <cuiyouzhi0...@gmail.com> 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.
>
> Do not confuse the man.

Do not confuse him further.

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

True enough.

> and therefore memset() can be safely used to produce
> null pointers

Not the case.

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

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

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>

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within

Michael Foukarakis

unread,
Jan 8, 2010, 4:05:51 AM1/8/10
to
On Jan 8, 10:31 am, Richard Heathfield <r...@see.sig.invalid> wrote:
> Michael Foukarakis wrote:
> > On Jan 6, 9:24 pm, Seebs <usenet-nos...@seebs.net> wrote:
> >> On 2010-01-06, John <cuiyouzhi0...@gmail.com> 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.
>
> > Do not confuse the man.
>
> 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
> > 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."
>
> True enough.
>
> > and therefore memset() can be safely used to produce
>
>  > null pointers
>
> Not the case.
>
>  > 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).
>
> 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!).

Michael Foukarakis

unread,
Jan 8, 2010, 4:09:27 AM1/8/10
to
On Jan 8, 10:25 am, Keith Thompson <ks...@mib.org> wrote:

> Michael Foukarakis <electricde...@gmail.com> writes:
> > On Jan 7, 1:44 am, Ben Bacarisse <ben.use...@bsb.me.uk> wrote:
> >> 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!)
>
> 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.

Richard Heathfield

unread,
Jan 8, 2010, 6:17:35 AM1/8/10
to
Michael Foukarakis wrote:

<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. :)

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?


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

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

Michael Foukarakis

unread,
Jan 8, 2010, 6:27:03 AM1/8/10
to
On Jan 8, 1:17 pm, Richard Heathfield <r...@see.sig.invalid> wrote:
> Michael Foukarakis wrote:
>
> <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. :)
>
> 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?
>
> > 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!).
>
> 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. :-)

David Thompson

unread,
Jan 14, 2010, 3:32:43 AM1/14/10
to
On Tue, 05 Jan 2010 21:10:16 -0800, Keith Thompson <ks...@mib.org>
wrote:

> John <cuiyou...@gmail.com> writes:

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

David Thompson

unread,
Jan 22, 2010, 5:20:06 AM1/22/10
to
On Fri, 08 Jan 2010 00:25:45 -0800, Keith Thompson <ks...@mib.org>
wrote:

> Michael Foukarakis <electr...@gmail.com> writes:

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

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.

0 new messages