In article <66**********************************@z66g2000hsc. googlegroups.com>
mdh <md**@comcast.netwrote:
>Not to flog a dead horse, does this then mean that when a void pointer
is passed to function (foo) expecting a character pointer, foo simply
cannot tell the difference between what it is expecting and what it is
getting, or that the compiler does not complain as **it** knows that
there is no difference between the two and thus allows this step to
occur without any warning?
Others have given various explanations but I would like to attempt
one that may help even more.
Think of each invocation of a C compiler as a request for a whole
new person to come in and do some work.
% cc -c a.c
% cc -c b.c
% cc -c c.c
% cc -o myprog a.o b.o c.o
The first time you run it, you get Alice. She comes in and
looks at a.c. She believes everything it says, and produces a.o.
The next time, though, you get Bob. He comes in and looks at b.c.
He believes everything it says, and produces b.o.
Your third command brings in Christine, who looks at c.c and
produces c.o.
Finally, your last one brings in Dave, who looks only at a.o, b.o,
and c.o and produces your program "myprog".
In principle, Alice, Bob, and Christine could even be in different
countries, and all working at the same time without ever meeting
or knowing each other. Dave, who does what is usually called the
"link" phase, does at least get to see whatever work-notes they
leave behind, but not the source files.
So, when you say "a void pointer is passed", "foo is expecting",
and "the compiler ... it knows", we have a problem. Is Alice doing
the passing? Is Bob doing the expecting? There are four separate
"compilers" here. Which one(s) know what?
Since -- at least in principle -- none of these separate invocations
needs to communicate much with each other, and even "Dave" (the
linker) may not get to see much, because Standard C requires very
few "work notes" to be left for him, it is your responsibility, as
the C programmer, to make sure you never tell Alice, Bob, or
Christine any lies. Everything that a.c claims about b.c and c.c
had better be true and correct; everything that b.c claims about
a.c had better be true and correct; and everything that c.c claims
about a.c and b.c had better be true and correct.
The easiest way to do this is to use header files. Have all three
".c" files pull in some common header(s): all.h; or a.h, b.h, and
c.h; or some combination like this. Make sure that any functions
that a.c exposes (i.e., makes public) are declared in a.h or all.h
or whatever, *and* that a.c #includes that file, so that Alice,
who works on a.c, will compare the "public claim" in a.h against
the actual function in a.c. This lets Alice tell you if you goofed
here.
There are two main things you can claim about a function, when you
give the function's name:
- its return type, and
- the number and type(s) of its arguments (to the extent that
those arguments are fixed, at least -- this brings up a third
thing you can claim, i.e., "all fixed arguments" vs "variadic").
To make both claims, use a prototype. Using a non-prototype
declaration like:
double blah();
makes only a claim about its return type. (Well, it also makes
the claim that the arguments are non-variadic, but it makes this
very weakly.) Unlike another apparently-similar language, if
blah() takes no arguments at all, you *must* put "void" inside
the parentheses to say this:
double blah0(void); /* blah0 takes exactly 0 arguments */
double blah2(double, char *); /* blah2 takes 2 args, as shown */
If the only thing any particular compiler / person (Alice, Bob,
or Christine in this case) sees about a function is a prototype,
she, he, or it will simply believe what the prototype says. So
if Alice thinks blah2() needs one "double" and one "char *",
and you do this:
double x = blah2(42, 0);
Alice will assume that you meant to send 42.0 and (char *)NULL to
blah2(), and will arrange for that to happen.
She need not leave any note for Dave to say that she did this, so
if blah2() is in b.c or c.c, and does not *actually* take one
"double" and one "char *", bad things may happen later. But if
blah2() is in b.c, and the prototype for blah2() is in b.h, and
b.c says to #include "b.h", then at least Bob will get a chance to
spot the mistake in b.h, and tell you. And then when you correct
b.h, you will -- or at least "should" -- know to ask Alice (or
maybe it will be Arnold) to compile a.c again, as well as getting
Bob (or Belinda this time) to compile b.c.
(You should know this because you should be keeping track of the
fact that a.c depends on b.h, so that if you touch b.h, you must
recompile a.c. Some build systems will do this automatically for
you. Some require a bit of manual work on a "makefile" and/or a
"make depend" step, and some really primitive systems make you
remember everything yourself and re-type your compilation commands
every time.)
In a particularly nice system, "Alice" would leave a note (in a.o)
for "Dave" (the linker) saying "by the way, I assumed this that
and the other thing" and Dave would check her notes against everyone
else's, and tell you if you got stuff wrong. Systems this nice
are still pretty rare, unfortunately.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: gmail (figure it out)
http://web.torek.net/torek/index.html