const void * to const unsigned char (*)[2]

R

RjY

I have a function whose (simplified) prototype looks like this:[1]

const void *get_data(int id);

For some values of id it is convenient to treat the resulting data as
pairs of bytes. To do this the existing code tries to cast the return
value to a pointer to an array of constant pairs of bytes

const unsigned char (*data)[2] = (void *)get_data(id);

and accesses using data[0] and data[1] for the byte pairs.

I thought it was odd that the const qualifier was being cast away, so I
took the cast out and tried to recompile. The compiler (GCC) then
reported

warning: initialization discards qualifiers from pointer target type

So, what's going on here? Why can't I assign a const void pointer to a
pointer to pairs of const unsigned chars? What's the proper thing to do?
Can I use a struct, such as

const struct { unsigned char b1,b2; } *data = get_data(id);

Is it guaranteed that there won't be padding between b1 and b2?
Or perhaps an array within a struct:

const struct { unsigned b[2]; } *data = get_data(id);

Both of these appear to work, at least on the machine in front of me,
but I would appreciate your guidance on what the standard has to say.
Thanks in advance.



[1] Implementation detail: given some identifier the function returns a
pointer to some arbitrary data in a memory-mapped container file, which
was opened read-only. Hence the const void * return type. However I hope
this is irrelevant.
 
M

Michael Tsang

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
I have a function whose (simplified) prototype looks like this:[1]

const void *get_data(int id);

For some values of id it is convenient to treat the resulting data as
pairs of bytes. To do this the existing code tries to cast the return
value to a pointer to an array of constant pairs of bytes

const unsigned char (*data)[2] = (void *)get_data(id);
You don't need the (void *) cast in legal C (but not C++). The constness
need not to be changed
and accesses using data[0] and data[1] for the byte pairs.

I thought it was odd that the const qualifier was being cast away, so I
took the cast out and tried to recompile. The compiler (GCC) then
reported

warning: initialization discards qualifiers from pointer target type

This warning is eliminated when the cast is omitted.
So, what's going on here? Why can't I assign a const void pointer to a
pointer to pairs of const unsigned chars? What's the proper thing to do?
Can I use a struct, such as

const struct { unsigned char b1,b2; } *data = get_data(id);

Is it guaranteed that there won't be padding between b1 and b2?
Or perhaps an array within a struct:

const struct { unsigned b[2]; } *data = get_data(id);

Both of these appear to work, at least on the machine in front of me,
but I would appreciate your guidance on what the standard has to say.
Thanks in advance.



[1] Implementation detail: given some identifier the function returns a
pointer to some arbitrary data in a memory-mapped container file, which
was opened read-only. Hence the const void * return type. However I hope
this is irrelevant.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)

iEYEARECAAYFAksmSEgACgkQG6NzcAXitM+zpgCfS0/mOmeqgg5EyRaP5zdp9tF2
GT4AnifBoc8NxK8dN4Ng/T2FLKVxVjcf
=+ITY
-----END PGP SIGNATURE-----
 
R

RjY

Michael Tsang posted:
RjY said:
const unsigned char (*data)[2] = (void *)get_data(id);
You don't need the (void *) cast in legal C (but not C++). The constness
need not to be changed

That's what I thought.
This warning is eliminated when the cast is omitted.

That's the opposite of what I'm seeing, though. The warning appears
unless I cast the function's return value to (void *), casting away the
const.

$ cat a.c
const void *get_data(int id);

int main(void)
{
#ifdef WITH_CAST
const unsigned char (*data)[2] = (void *)get_data(0);
#endif

#ifdef NO_CAST
const unsigned char (*data)[2] = get_data(0);
#endif

return 0;
}
$ gcc -ansi -pedantic -DWITH_CAST -c a.c
$ gcc -ansi -pedantic -DNO_CAST -c a.c
a.c: In function ‘main’:
a.c:10: warning: initialization discards qualifiers from pointer target type
 
E

Eric Sosman

I have a function whose (simplified) prototype looks like this:[1]

const void *get_data(int id);

For some values of id it is convenient to treat the resulting data as
pairs of bytes. To do this the existing code tries to cast the return
value to a pointer to an array of constant pairs of bytes

const unsigned char (*data)[2] = (void *)get_data(id);

and accesses using data[0] and data[1] for the byte pairs.

I thought it was odd that the const qualifier was being cast away, so I
took the cast out and tried to recompile. The compiler (GCC) then
reported

warning: initialization discards qualifiers from pointer target type

So, what's going on here? Why can't I assign a const void pointer to a
pointer to pairs of const unsigned chars?


This seems related to Question 11.10 in the comp.lang.c
Frequently Asked Questions (FAQ) list, <http://www.c-faq.com/>.
It's not a precise match, but I think it's fundamentally the
same issue.
What's the proper thing to do?
Can I use a struct, such as

const struct { unsigned char b1,b2; } *data = get_data(id);

Is it guaranteed that there won't be padding between b1 and b2?

No: There can be padding after either or both of b1 and b2.
Or perhaps an array within a struct:

const struct { unsigned b[2]; } *data = get_data(id);

That would work for the first pair, but there could be
padding after b which would mess things up for subsequent pairs.
Both of these appear to work, at least on the machine in front of me,
but I would appreciate your guidance on what the standard has to say.

I'm not going to offer guidance on the Standard's language,
because I don't understand this part of it all that well myself.
As for the code, though: Keep the (void*) cast, and just live
with the fact that the compiler cannot police everything you do.
 
B

Ben Bacarisse

RjY said:
I have a function whose (simplified) prototype looks like this:[1]

const void *get_data(int id);

For some values of id it is convenient to treat the resulting data as
pairs of bytes. To do this the existing code tries to cast the return
value to a pointer to an array of constant pairs of bytes

const unsigned char (*data)[2] = (void *)get_data(id);

and accesses using data[0] and data[1] for the byte pairs.

I thought it was odd that the const qualifier was being cast away, so I
took the cast out and tried to recompile. The compiler (GCC) then
reported

warning: initialization discards qualifiers from pointer target
type


Well, it does. The problem is that you can't have a const array.
There is just no syntax for it (unless I've missed it) so a pointer to
an array is always to something that is not const. The fact that all
the elements of the array are both const does make the array itself
const.
So, what's going on here? Why can't I assign a const void pointer to a
pointer to pairs of const unsigned chars? What's the proper thing to
do?

I think you have to live with a cast or a warning (or both, in fact
since with enough warnings turned on, gcc complains about both your
examples).
Can I use a struct, such as

const struct { unsigned char b1,b2; } *data = get_data(id);

Is it guaranteed that there won't be padding between b1 and b2?

No, there might be (and after b2).
Or perhaps an array within a struct:

const struct { unsigned b[2]; } *data = get_data(id);

Both of these appear to work, at least on the machine in front of me,
but I would appreciate your guidance on what the standard has to
say.

There can be padding after b. You can test for this at compile time
so, for example, if you have:

typedef int static_assert_dummy_array[sizeof(struct pair) == 2 ? 1 : -1];

you will get a warning (or more likely an error) if there is padding
after b, but I think I'd just accept the cast.
 
P

Peter Nilsson

I have a function whose (simplified) prototype looks like
this:[1]

  const void *get_data(int id);

For some values of id it is convenient to treat the resulting
data as pairs of bytes. To do this the existing code tries to
cast the return value to a pointer to an array of constant
pairs of bytes

  const unsigned char (*data)[2] = (void *)get_data(id);

and accesses using data[0] and data[1] for the byte
pairs.


Declaring a pointer to an array whose elements are not arrays,
is almost always a headache.

Just treat it as a byte stream and read two at a time.

const unsigned char *data = get_data(id);
 
K

Kaz Kylheku

I have a function whose (simplified) prototype looks like this:[1]

const void *get_data(int id);

For some values of id it is convenient to treat the resulting data as
pairs of bytes. To do this the existing code tries to cast the return
value to a pointer to an array of constant pairs of bytes

const unsigned char (*data)[2] = (void *)get_data(id);

Note that this is not a pointer to const char, but
to a pointer to an /unqualified/ array, of two const char.

The pointer is to an unqualified type (which is an aggregaet
that has qualified members).

So the conversion does in fact strip qualifiers from const void *.
 

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

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top