assignment-compatible.
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,