Advanced pointer typecasting

Discussion in 'C++' started by Robert Street, Feb 20, 2004.

  1. Hi!

    I'm rather new at c++ and I'm totally confused with this kind of
    typecasting:

    typedef signed char int8_t;
    typedef signed short int16_t;

    typedef struct broadcast_hdr
    {
    broadcast_type_t broadcast_type;
    uint16_t items_n;
    uint16_t size_n;
    } broadcast_hdr_t;

    typedef struct broadcast_type
    {
    char ac;
    char bc;
    uint16_t number_n;
    } broadcast_type_t;

    typedef struct ob_levels_price
    {
    uint16_t maskA_n;
    uint16_t maskB_n;
    uint8_t levels_c;
    uint8_t items_c;
    char filler_2_s [2];
    } ob_levels_price_t;

    void HandleBroadcast(int8_t* broadcast, uint16_t length)
    {
    static broadcast_type_t typeA = {'B','I',9};
    static broadcast_type_t typeB = {'B','D',6};
    static broadcast_type_t typeC = {'B','O',14};

    broadcast_type_t* bc;

    bc = (broadcast_type_t*)broadcast;

    if (memcmp(bc, &typeC, sizeof(broadcast_type_t)) == 0)
    {
    SpecificBroadcast(broadcast, length);
    return;
    }

    }

    void SpecificBroadcast(int8_t* broadcast, uint16_t length)
    {

    ob_levels_price_t* obPriceVolumes = (ob_levels_price_t*)broadcast;

    broadcast_hdr_t* bc = (broadcast_hdr_t*)broadcast;
    uint32_t substart = sizeof(*bc);
    uint8_t *pBc = (uint8_t*)bc;
    }

    Can someone explain to me SpecificBroadcast. I don't understand how
    the contents of an signed char (int8_t) can be converted to a struct
    (ob_levels_price_t, broadcast_hdr_t) through its address. What could
    possibly be the contents of the broadcast variable???

    Really stumped.

    Thanks in advance.
    Robert Street, Feb 20, 2004
    #1
    1. Advertising

  2. Robert Street

    Jack Klein Guest

    On 19 Feb 2004 19:01:35 -0800, (Robert Street)
    wrote in comp.lang.c++:

    > Hi!
    >
    > I'm rather new at c++ and I'm totally confused with this kind of
    > typecasting:


    You should be, it almost certainly indicates bad design. It depends
    on one guaranteed feature of the language, and a lot of things that
    are not guaranteed.

    One guarantee that the language makes is that a pointer to any type of
    object can be converted, with a suitable cast, to a pointer to one of
    the character types or to a pointer to void. If such a pointer
    originally points to a valid object of its type, the char or void
    pointer can later be cast back to a pointer of the original object
    type and it will still point to the same object.

    There is no such guarantee for any other type of pointer, as other
    types of headers may use fewer bits of information than pointer to
    char or void.

    There is most certainly no guarantee that just any arbitrary pointer
    to char or void can be cast to a pointer to another object type and be
    valid. It might not have the proper alignment for the other object
    type, and cause undefined behavior when used to access that type.

    > typedef signed char int8_t;
    > typedef signed short int16_t;
    >
    > typedef struct broadcast_hdr
    > {
    > broadcast_type_t broadcast_type;
    > uint16_t items_n;
    > uint16_t size_n;
    > } broadcast_hdr_t;
    >
    > typedef struct broadcast_type
    > {
    > char ac;
    > char bc;
    > uint16_t number_n;
    > } broadcast_type_t;
    >
    > typedef struct ob_levels_price
    > {
    > uint16_t maskA_n;
    > uint16_t maskB_n;
    > uint8_t levels_c;
    > uint8_t items_c;
    > char filler_2_s [2];
    > } ob_levels_price_t;
    >
    > void HandleBroadcast(int8_t* broadcast, uint16_t length)
    > {
    > static broadcast_type_t typeA = {'B','I',9};
    > static broadcast_type_t typeB = {'B','D',6};
    > static broadcast_type_t typeC = {'B','O',14};
    >
    > broadcast_type_t* bc;
    >
    > bc = (broadcast_type_t*)broadcast;
    >
    > if (memcmp(bc, &typeC, sizeof(broadcast_type_t)) == 0)


    The line above is very problematic. Even if "broadcast" was obtained
    by casting the address a broadcast_type_t to pointer to signed char,
    so there are no alignment issues, and even if the original
    broadcast_type_t contained {'B', 'O', 14 }, there is no requirement
    that the two structures compare equal when passed to memcmp().

    Padding is allowed in structures everywhere except before the first
    member. There can be padding in between members, and after the last
    member. The value of the padding bytes can be different in two
    structures whose actual members are identical, causing the memcmp() to
    fail.

    > {
    > SpecificBroadcast(broadcast, length);
    > return;
    > }
    >
    > }
    >
    > void SpecificBroadcast(int8_t* broadcast, uint16_t length)
    > {
    >
    > ob_levels_price_t* obPriceVolumes = (ob_levels_price_t*)broadcast;
    >
    > broadcast_hdr_t* bc = (broadcast_hdr_t*)broadcast;
    > uint32_t substart = sizeof(*bc);
    > uint8_t *pBc = (uint8_t*)bc;
    > }
    >
    > Can someone explain to me SpecificBroadcast. I don't understand how
    > the contents of an signed char (int8_t) can be converted to a struct
    > (ob_levels_price_t, broadcast_hdr_t) through its address. What could
    > possibly be the contents of the broadcast variable???
    >
    > Really stumped.
    >
    > Thanks in advance.


    This code uses C casts, although a C++ static_cast could do the same
    job.

    As I said above, a pointer to void or one of the character types can
    hold a pointer to any type of object. Unless the pointer originated
    as a pointer to that type of object, you get undefined behavior if you
    try to access an object of that type through it.

    There is nothing particularly C++ about this code, it is completely
    compatible with C. But it is a bad design in either language. There
    are much more sensible, and portable, ways to do whatever this code
    does, such as putting the structure types in a union and passing a
    pointer to the union.

    The code also depends on specifically non-portable behavior, the fact
    that on some implementation there will be no padding in the
    broadcast_type_t structure so that two structures with
    member-by-member equality will compare equal when passed to memcmp().
    There is no such guarantee in either C or C++, and there are compilers
    where the memcmp() might fail a good part of the time even if all the
    individual members of the two structures compare equal.

    This code is bad, and should not be emulated.

    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++
    http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
    Jack Klein, Feb 20, 2004
    #2
    1. Advertising

  3. "Robert Street" <> wrote in message
    news:...
    > Hi!
    >
    > I'm rather new at c++ and I'm totally confused with this kind of
    > typecasting:
    >
    > typedef signed char int8_t;
    > typedef signed short int16_t;


    snip

    > void SpecificBroadcast(int8_t* broadcast, uint16_t length)
    > {
    >
    > ob_levels_price_t* obPriceVolumes = (ob_levels_price_t*)broadcast;
    >
    > broadcast_hdr_t* bc = (broadcast_hdr_t*)broadcast;
    > uint32_t substart = sizeof(*bc);
    > uint8_t *pBc = (uint8_t*)bc;
    > }
    >
    > Can someone explain to me SpecificBroadcast. I don't understand how
    > the contents of an signed char (int8_t) can be converted to a struct
    > (ob_levels_price_t, broadcast_hdr_t) through its address. What could
    > possibly be the contents of the broadcast variable???
    >


    This is what people always used to do for low level i/o stuff before void*.
    Although they usually use a plain old char*

    Some people still do this because when you are doing low level stuff you
    often need to do byte address calculations
    and that requires a pointer to something of size 1. e.g.

    // read a message from a socket
    void readn(int fd,void* buf,int size)
    {
    do
    {
    int nread = read(fd,buf,size); // read should take void*
    size -= nread;
    buf = (char*)buf + nread; // cast required purely to do
    arithmetic
    } while(size);
    }

    Even then it is best to make the function parameter void* because although
    making it 'byte'* would make the function simpler
    it would often force the caller to cast to something it is not.
    eg.
    ob_levels_price_t x;
    SpecificBroadcast(&x,sizeof(x)); // will not coompile despite the fact
    that it clearly should according to SpecificBroadcast
    SpecificBroadcast((uint8_t*)&x,sizeof(x)); // will compile - using
    reinterpret_cast only makes it uglier

    Another good reason to use void* rather than some sort of char* is this is
    that ostream<< will do the wrong thing and debuggers will spew garbage when
    doing stack dumps or showing local vars.

    IMHO It would be a nice idea to allow pointer arithmetic on void* as if it
    was a pointer to 'byte' - I don't think it would
    do any harm in real programs.
    Nick Hounsome, Feb 20, 2004
    #3
  4. "Nick Hounsome" <> wrote in message news:<9wrZb.9287$>...
    > "Robert Street" <> wrote in message
    > news:...
    > > Hi!
    > >
    > > I'm rather new at c++ and I'm totally confused with this kind of
    > > typecasting:
    > >
    > > typedef signed char int8_t;
    > > typedef signed short int16_t;

    >
    > snip
    >
    > > void SpecificBroadcast(int8_t* broadcast, uint16_t length)
    > > {
    > >
    > > ob_levels_price_t* obPriceVolumes = (ob_levels_price_t*)broadcast;
    > >
    > > broadcast_hdr_t* bc = (broadcast_hdr_t*)broadcast;
    > > uint32_t substart = sizeof(*bc);
    > > uint8_t *pBc = (uint8_t*)bc;
    > > }
    > >
    > > Can someone explain to me SpecificBroadcast. I don't understand how
    > > the contents of an signed char (int8_t) can be converted to a struct
    > > (ob_levels_price_t, broadcast_hdr_t) through its address. What could
    > > possibly be the contents of the broadcast variable???
    > >

    >
    > This is what people always used to do for low level i/o stuff before void*.
    > Although they usually use a plain old char*
    >
    > Some people still do this because when you are doing low level stuff you
    > often need to do byte address calculations
    > and that requires a pointer to something of size 1. e.g.
    >
    > // read a message from a socket
    > void readn(int fd,void* buf,int size)
    > {
    > do
    > {
    > int nread = read(fd,buf,size); // read should take void*
    > size -= nread;
    > buf = (char*)buf + nread; // cast required purely to do
    > arithmetic
    > } while(size);
    > }
    >
    > Even then it is best to make the function parameter void* because although
    > making it 'byte'* would make the function simpler
    > it would often force the caller to cast to something it is not.
    > eg.
    > ob_levels_price_t x;
    > SpecificBroadcast(&x,sizeof(x)); // will not coompile despite the fact
    > that it clearly should according to SpecificBroadcast
    > SpecificBroadcast((uint8_t*)&x,sizeof(x)); // will compile - using
    > reinterpret_cast only makes it uglier
    >


    Thanks for the help.

    One more question, why can the code cast the uint8_t pointer to *both*
    ob_levels_price_t* and broadcast_header_t*? Shouldn't the original
    address be simply pointing to one type of struct? Any ideas?

    > Another good reason to use void* rather than some sort of char* is this is
    > that ostream<< will do the wrong thing and debuggers will spew garbage when
    > doing stack dumps or showing local vars.
    >
    > IMHO It would be a nice idea to allow pointer arithmetic on void* as if it
    > was a pointer to 'byte' - I don't think it would
    > do any harm in real programs.
    Robert Street, Feb 21, 2004
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. venkatesh
    Replies:
    1
    Views:
    8,111
    lallous
    Dec 6, 2003
  2. Arun Prasath
    Replies:
    2
    Views:
    336
    Peter Shaggy Haywood
    Nov 26, 2003
  3. dis
    Replies:
    2
    Views:
    1,457
    Eric Sosman
    Jun 11, 2004
  4. Abhishek
    Replies:
    16
    Views:
    823
    Jordan Abel
    Jan 26, 2006
  5. Michele Simionato
    Replies:
    1
    Views:
    583
    Lacrima
    Mar 27, 2010
Loading...

Share This Page