To coerce or not...? struct sockaddr vs struct sockaddr_in

Discussion in 'C Programming' started by James Harris, Oct 4, 2003.

  1. James Harris

    James Harris Guest

    Having updated my Debian system it now complains that I am using
    an incompatible pointer type in warnings such as "passing arg 2
    of 'bind' from incompatible pointer type" from code,

    struct sockaddr_in sockad1;
    ....
    retval = bind (sock1, &sockad1, sockad1len);

    I can coerce the pointer with

    retval = bind (sock1, (struct sockaddr *) &sockad1, sockad1len);

    but that seems to me to be bad style - doesn't it overrides the
    compiler's checking? I cannot instead change the defintion to
    struct sockaddr sockad1 because my code uses members of the
    sockaddr_in structure. Any other resolution I can think of is
    messy. Why cannot the compiler accept that a pointer to a struct
    sockaddr_in IS compatible with a pointer to struct sockaddr?
    Should I coerce to keep the compiler happy or is there a better
    way to approch the problem?

    TIA,
    - James
     
    James Harris, Oct 4, 2003
    #1
    1. Advertising

  2. James Harris

    Kevin Easton Guest

    James Harris <no.email.please> wrote:
    > Having updated my Debian system it now complains that I am using
    > an incompatible pointer type in warnings such as "passing arg 2
    > of 'bind' from incompatible pointer type" from code,
    >
    > struct sockaddr_in sockad1;
    > ...
    > retval = bind (sock1, &sockad1, sockad1len);
    >
    > I can coerce the pointer with
    >
    > retval = bind (sock1, (struct sockaddr *) &sockad1, sockad1len);
    >
    > but that seems to me to be bad style - doesn't it overrides the
    > compiler's checking?


    Yes, it is. But the bad style is not yours; it is the sockets API
    you're using that is the root cause (it should have used a pointer to
    union instead).

    > I cannot instead change the defintion to
    > struct sockaddr sockad1 because my code uses members of the
    > sockaddr_in structure. Any other resolution I can think of is
    > messy. Why cannot the compiler accept that a pointer to a struct
    > sockaddr_in IS compatible with a pointer to struct sockaddr?


    Because C doesn't work that way. Pointers to different structs are
    never assignment-compatible.

    > Should I coerce to keep the compiler happy or is there a better
    > way to approch the problem?


    Since you can't change the signature of the function, the cast is the
    best solution. If you're concerned about the possibility of the cast
    hiding errors, you can reduce it to one instance of the cast in your
    entire code (I'm making an educated guess as to the signature of the
    bind function you're calling):

    int bind_in(int sockfd, struct sockaddr_in *my_addr, socklen_t addrlen)
    {
    return bind(sockfd, (struct sockaddr *)my_addr, addrlen);
    }

    Then just call bind_in() instead of bind() whenever you're using a
    sockaddr_in. The only cast to get right is the one in bind_in() - the
    compiler will warn you if you try to pass anything other than struct
    sockaddr_in * as the second parameter of bind_in().

    - Kevin.
     
    Kevin Easton, Oct 4, 2003
    #2
    1. Advertising

  3. James Harris

    James Harris Guest

    Kevin, Many thanks for your help. It all makes a bit more sense
    now!

    "Kevin Easton" <> wrote in message
    news:newscache$pxo8mh$ba2$...
    <snip>

    > Because C doesn't work that way. Pointers to different

    structs are
    > never assignment-compatible.


    Does this just happen for structs? The third parameter seems to
    accept any of int, long etc and perversely char, float and
    double, as well as any size_X parameter. How can bind be
    expected to make sense of a float???

    I notice from the headers that socklen_t is defined from
    __socklen_t which is declared as typedef unsigned int. Would I
    be right in assuming that the compiler generates code to convert
    between float and unsigned int? The code works with this as a
    float. Horrible.

    ....I have since been looking at the generated assembler and
    compilation of the line
    retval = bind (sock1, (struct sockaddr *) &mysock, mysocklen);
    generates the following in gcc 2.95.4. The line with a - is the
    line used when mysocklen is an integer, a long, or a socklen_t
    (all compile identically). The lines with a + replace that line
    when s1len is declared as a float. I think the fistpll writes
    the float as an integer, which goes in to EDX before finding its
    way to EAX and being pushed, thus converting before making the
    call. Note to self: if you want fast code be careful to use
    integer if that's what your library calls expect.(!)
    addl $-4,%esp
    - movl -152(%ebp),%eax
    + flds -152(%ebp)
    + fnstcw -670(%ebp)
    + movw -670(%ebp),%ax
    + orw $3072,%ax
    + movw %ax,-672(%ebp)
    + fldcw -672(%ebp)
    + fistpll -680(%ebp)
    + movl -680(%ebp),%edx
    + movl -676(%ebp),%ecx
    + fldcw -670(%ebp)
    + movl %edx,%eax
    pushl %eax
    leal -116(%ebp),%eax
    pushl %eax
    movl -512(%ebp),%eax
    pushl %eax
    call bind
    addl $16,%esp
    movl %eax,%eax
    movl %eax,-56(%ebp)
     
    James Harris, Oct 6, 2003
    #3
  4. James Harris

    Chris Torek Guest

    >"Kevin Easton" <> wrote in message
    >news:newscache$pxo8mh$ba2$...
    >>... Pointers to different structs are never assignment-compatible.


    In article <3f81e0a5$0$10967$>,
    James Harris <no.email.please> wrote:
    >Does this just happen for structs?


    Structures and unions (and "enum"s, to some extent). The reason
    that "pointer to struct X" and "pointer to struct Y" are not
    compatible is that the first occurrence of "struct X" creates a
    new type -- the type named "struct X" -- and then "struct Y" creates
    another new type, the one named "struct Y". Since these are two
    new types, they must be different types, even if the contents are
    the same.

    As Kevin Easton said, the socket interface is not very well designed,
    and as a result you *must* cast (or take a trip through "void *")
    in at least one place. (The type-safety, or lack thereof, is just
    one aspect of where the original BSD socket interface goes wrong.
    If I could go back in time and fix it, I would try to convince
    someone to replace the bind(), connect(), and listen() calls with
    a single call. But this is different problem entirely, having
    nothing to do with C per se.)

    >The third parameter seems to
    >accept any of int, long etc and perversely char, float and
    >double, as well as any size_X parameter. How can [function F] be
    >expected to make sense of a float???


    The actual name of "function F" is not relevant here but its
    prototype, if it has one -- and apparently it does on your system
    -- is. That prototype is:

    typedef unsigned int some_integral_type; /* as it happens */
    int F(int, struct S *, some_integral_type);

    In C, any call to a function that has a prototype in scope at
    the point of the call makes use of the types in the prototype.
    They cause each actual parameter in the call to be passed "as
    if by ordinary assignment".

    If you write:

    int i;
    float f = 3.14;

    and then write:

    i = f;

    this instructs the compiler to truncate the ".14" part, leaving
    just 3, and assigning 3 to i -- using whatever machine code sequence
    is required to do that. (If the compiler can prove to itself that
    this sets i to 3, this could be as simple as "move constant_3 =>
    register holding i" or some such, rather than the 11 lines of
    x86 assembly I will refrain from quoting. :) )

    Since a prototyped call is "just like" assignment, a call of the
    form:

    extern void zog(int);
    zog(f);

    must likewise "assign" 3 to zog's formal parameter:

    void zog(int i) { printf("zog got %d\n", i); }

    On the other hand, if you omit the prototype, or fail to declare
    zog() at all, then -- in C89 only -- no diagnostic is required and
    the behavior is undefined. (In C99 "failure to declare zog()"
    requires a diagnostic; declaring it without a prototype still
    produces undefined behavior.) In practice, C compilers treat
    this in the same way as calls to prototyped-but-variadic functions,
    so that:

    extern void zog(); /* note lack of prototype */
    zog(f);

    widens the value in f (still 3.14) to "double" -- changing its
    representation in memory and/or registers if needed, as is the case
    on the x86 -- and this works right only if zog() tries to access
    the provided "double". Grabbing an "int", when the compiler passed
    a "double", remains undefined, but does one of two different things
    in practice: it either gets sizeof(int) bytes out of the sizeof(double)
    bytes passed, or it gets an apparently-random value out of some
    unset memory (typically, a "wrong register" or "wrong stack" --
    e.g., an x86 compiler might pass the "double" on the FPU stack
    instead of the stack at %esp, so that the "int" zog() reads at
    (%esp+K) for some constant K has nothing to do with the 3.14 in
    the FPU stack).

    Ultimately, this boils down to a few simple rules C programmers
    can follow.

    - Always use complete prototypes whenever possible. Calls
    to prototyped functions act like ordinary assignments,
    in terms of the values passed in.

    - Beware of variadic functions, which effectively have
    no prototypes beyond the fixed-argument portion. The
    varying arguments are "widened", so that float becomes
    double, and char and short become int. The exact rules
    get a bit complicated (does "unsigned short" widen to
    signed int or unsigned int? this turns out to be
    implementation-dependent!), and C99 introduces a new
    concept of "type rank" along with its horde of new
    types (long long, and all the complex types).

    - Compile with the "warn me if I forgot to prototype"
    option, so that you get A, instead of B. :)

    - Define new types with "struct". The "typedef" keyword
    is misleading: it means "do not define a new type". :)
    (It defines aliases for *existing* types. In "typedef
    struct S { ... } alias;", it is the "struct" part that
    defines the new type.)
    --
    In-Real-Life: Chris Torek, Wind River Systems (BSD engineering)
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://67.40.109.61/torek/index.html (for the moment)
    Reading email is like searching for food in the garbage, thanks to spammers.
     
    Chris Torek, Oct 8, 2003
    #4
  5. James Harris

    James Harris Guest

    Chris,
    You've touched upon a number of issues which strike a chord but
    I'll maybe come back and raise some questions on them another
    time. Thanks for the help just now.
    - James


    "Chris Torek" <> wrote in message
    news:bm18t6$1h6$...
    > >"Kevin Easton" <> wrote in message
    > >news:newscache$pxo8mh$ba2$...
    > >>... Pointers to different structs are never

    assignment-compatible.
    >
    > In article <3f81e0a5$0$10967$>,
    > James Harris <no.email.please> wrote:
    > >Does this just happen for structs?

    >
    > Structures and unions (and "enum"s, to some extent). The

    reason
    > that "pointer to struct X" and "pointer to struct Y" are not
    > compatible is that the first occurrence of "struct X" creates

    a
    > new type -- the type named "struct X" -- and then "struct Y"

    creates
    > another new type, the one named "struct Y". Since these are

    two
    > new types, they must be different types, even if the contents

    are
    > the same.
    >
    > As Kevin Easton said, the socket interface is not very well

    designed,
    > and as a result you *must* cast (or take a trip through "void

    *")
    > in at least one place. (The type-safety, or lack thereof, is

    just
    > one aspect of where the original BSD socket interface goes

    wrong.
    > If I could go back in time and fix it, I would try to convince
    > someone to replace the bind(), connect(), and listen() calls

    with
    > a single call. But this is different problem entirely, having
    > nothing to do with C per se.)
    >
    > >The third parameter seems to
    > >accept any of int, long etc and perversely char, float and
    > >double, as well as any size_X parameter. How can [function F]

    be
    > >expected to make sense of a float???

    >
    > The actual name of "function F" is not relevant here but its
    > prototype, if it has one -- and apparently it does on your

    system
    > -- is. That prototype is:
    >
    > typedef unsigned int some_integral_type; /* as it happens

    */
    > int F(int, struct S *, some_integral_type);
    >
    > In C, any call to a function that has a prototype in scope at
    > the point of the call makes use of the types in the prototype.
    > They cause each actual parameter in the call to be passed "as
    > if by ordinary assignment".
    >
    > If you write:
    >
    > int i;
    > float f = 3.14;
    >
    > and then write:
    >
    > i = f;
    >
    > this instructs the compiler to truncate the ".14" part,

    leaving
    > just 3, and assigning 3 to i -- using whatever machine code

    sequence
    > is required to do that. (If the compiler can prove to itself

    that
    > this sets i to 3, this could be as simple as "move constant_3

    =>
    > register holding i" or some such, rather than the 11 lines of
    > x86 assembly I will refrain from quoting. :) )
    >
    > Since a prototyped call is "just like" assignment, a call of

    the
    > form:
    >
    > extern void zog(int);
    > zog(f);
    >
    > must likewise "assign" 3 to zog's formal parameter:
    >
    > void zog(int i) { printf("zog got %d\n", i); }
    >
    > On the other hand, if you omit the prototype, or fail to

    declare
    > zog() at all, then -- in C89 only -- no diagnostic is required

    and
    > the behavior is undefined. (In C99 "failure to declare zog()"
    > requires a diagnostic; declaring it without a prototype still
    > produces undefined behavior.) In practice, C compilers treat
    > this in the same way as calls to prototyped-but-variadic

    functions,
    > so that:
    >
    > extern void zog(); /* note lack of prototype */
    > zog(f);
    >
    > widens the value in f (still 3.14) to "double" -- changing its
    > representation in memory and/or registers if needed, as is the

    case
    > on the x86 -- and this works right only if zog() tries to

    access
    > the provided "double". Grabbing an "int", when the compiler

    passed
    > a "double", remains undefined, but does one of two different

    things
    > in practice: it either gets sizeof(int) bytes out of the

    sizeof(double)
    > bytes passed, or it gets an apparently-random value out of

    some
    > unset memory (typically, a "wrong register" or "wrong

    stack" --
    > e.g., an x86 compiler might pass the "double" on the FPU stack
    > instead of the stack at %esp, so that the "int" zog() reads at
    > (%esp+K) for some constant K has nothing to do with the 3.14

    in
    > the FPU stack).
    >
    > Ultimately, this boils down to a few simple rules C

    programmers
    > can follow.
    >
    > - Always use complete prototypes whenever possible. Calls
    > to prototyped functions act like ordinary assignments,
    > in terms of the values passed in.
    >
    > - Beware of variadic functions, which effectively have
    > no prototypes beyond the fixed-argument portion. The
    > varying arguments are "widened", so that float becomes
    > double, and char and short become int. The exact rules
    > get a bit complicated (does "unsigned short" widen to
    > signed int or unsigned int? this turns out to be
    > implementation-dependent!), and C99 introduces a new
    > concept of "type rank" along with its horde of new
    > types (long long, and all the complex types).
    >
    > - Compile with the "warn me if I forgot to prototype"
    > option, so that you get A, instead of B. :)
    >
    > - Define new types with "struct". The "typedef" keyword
    > is misleading: it means "do not define a new type". :)
    > (It defines aliases for *existing* types. In "typedef
    > struct S { ... } alias;", it is the "struct" part that
    > defines the new type.)
    > --
    > In-Real-Life: Chris Torek, Wind River Systems (BSD

    engineering)
    > Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277

    2603
    > email: forget about it http://67.40.109.61/torek/index.html

    (for the moment)
    > Reading email is like searching for food in the garbage,

    thanks to spammers.
     
    James Harris, Oct 9, 2003
    #5
    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. Matthew Wilson
    Replies:
    8
    Views:
    285
    vbgunz
    Oct 2, 2006
  2. Alan Isaac

    disappearing documentation of `coerce`

    Alan Isaac, Jul 5, 2007, in forum: Python
    Replies:
    2
    Views:
    280
    Alan Isaac
    Jul 5, 2007
  3. Mark Tarver

    how to coerce to a string in Python?

    Mark Tarver, Apr 28, 2009, in forum: Python
    Replies:
    3
    Views:
    1,379
    Paul Rubin
    Apr 29, 2009
  4. John Fletcher

    Problem with coerce and SWIG

    John Fletcher, Jul 7, 2003, in forum: Ruby
    Replies:
    1
    Views:
    106
    John Fletcher
    Jul 8, 2003
  5. Asterbing
    Replies:
    9
    Views:
    367
    Asterbing
    Mar 27, 2006
Loading...

Share This Page