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

Discussion in 'C Programming' started by RjY, Dec 14, 2009.

  1. RjY

    RjY Guest

    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.
     
    RjY, Dec 14, 2009
    #1
    1. Advertisements

  2. -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1
    You don't need the (void *) cast in legal C (but not C++). The constness
    need not to be changed

    This warning is eliminated when the cast is omitted.
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.9 (GNU/Linux)

    iEYEARECAAYFAksmSEgACgkQG6NzcAXitM+zpgCfS0/mOmeqgg5EyRaP5zdp9tF2
    GT4AnifBoc8NxK8dN4Ng/T2FLKVxVjcf
    =+ITY
    -----END PGP SIGNATURE-----
     
    Michael Tsang, Dec 14, 2009
    #2
    1. Advertisements

  3. RjY

    RjY Guest

    Michael Tsang posted:
    That's what I thought.
    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
     
    RjY, Dec 14, 2009
    #3
  4. RjY

    Eric Sosman Guest



    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.
    No: There can be padding after either or both of b1 and b2.
    That would work for the first pair, but there could be
    padding after b which would mess things up for subsequent pairs.
    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.
     
    Eric Sosman, Dec 14, 2009
    #4


  5. 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.
    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).
    No, there might be (and after b2).
    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.
     
    Ben Bacarisse, Dec 14, 2009
    #5


  6. 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);
     
    Peter Nilsson, Dec 14, 2009
    #6
  7. RjY

    Kaz Kylheku Guest

    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 *.
     
    Kaz Kylheku, Dec 15, 2009
    #7
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.