No, it is not. Your analogy is flawed for two reasons:
1. Both of the different data pointers in my question are pointers to
structures (gzFile, known externally as void, and FILE), although
different structures. Such pointers are interchangable.
No, not according to the original posting anyway. You said that
one function took a "void *" pointer, the other a "FILE *".
Now, go find yourself a Data General Eclipse machine, and compile
the following source code:
#include <stdio.h>
int get1(void *vp) {
FILE *fp = vp;
return fgetc(vp);
}
Observe the assembler-level code (either disassemble the object code,
or compile to assembly). Note that the assignment "fp = vp" *uses
a shift instruction*. The assembly code turns out to be identical
to that for:
int get1(unsigned int vp) {
unsigned int fp = vp >> 1;
return fgetc(fp);
}
Now compare that to the assembly code for:
int get2(void *vp) {
return gzgetc(vp);
}
Here there is no shift instruction.
In other words, assignments from "FILE *" to "void *" require shifts
(left or right depending on which is the source and which is the
destination), while assignments from "void *" to "void *" are simply
bit-for-bit copies. You *must* get the shift instruction, via the
wrapper, for the version that calls fgetc(), because fgetc() expects
its argument to be pre-shifted.
2. I'm not trying to assign any values to the memory being pointed
to by these either (no dereferencing) -- just passing them down to
functions (gzgetc, fgetc), which know how to deal with them.
The gzgetc() function takes a "void *", and the fgetc() function
takes a "FILE *" (which is a "struct ... *"). These pointers have
different formats. The insides of both gzgetc() and fgetc() assign
values to the data structures to which the pointers point. These
assignments are hidden, but do occur.
There is also another pair of pointers in my example -- pointers to
functions (gzgetc and fgetc). This confusion may have led you to the flawed
analogy, but, unlike pointers to _different_ data-types, pointers to
functions are interchangable too.
Pointers to functions are *interconvertible*. This is not, at
least with my definition of "interchangeable", quite the same as
interchangeable. Consider int and double: we can take an int value,
convert it to a double, and (on most machines) preserve *all* the
original values (because int is typically 32 or fewer bits, while
double has at least 53 bits of mantissa). If we then convert the
resulting double back to int, we get the original value back --
but the conversion has changed things internally. The internal
representations of (int)3 and (double)3.0 are quite different (again,
on most machines[%] anyway).
-----
% The Burroughs A-series stored integers in floating-point format.
I have some doubts whether anyone could build a working C
implementation on one of these, even if any are still out there
to use, but if group did build a C compiler for one, they might
use the same representation for int and double.
-----
The same holds, in principle, for function pointers in C. The
standard guarantees that, given any two function types FT1 and FT2
and function f() of type FT1, the result of:
(FT1)(FT2)f
is just as useful as the original pointer. The "round trip
conversion" from FT1 to FT2, and then back to FT1, via the two
casts, preserves the value. The standard does *not* guarantee that
the internal representations are the same. Unlike the Eclipse
example, I do not know of any machines where the conversion really
does change the representation -- but it is *allowed*, so portable
code should be sure to cause both conversions. On the Eclipse,
when dealing with data pointers, both conversions are required;
code that achieves only one of them -- including incorrect qsort()
functions, which were one of the canonical examples -- fails at
runtime.
Most modern machines have only one internal representation for
pointers (the generic byte pointer), which has led people to forget
all the "interesting" problems that occurred when porting code to
the Eclipse, or even some of the funny "memory models" of the older
x86 (only some of which were actually standard-conforming). The
C standards (C89 and C99 both) do allow for them, though, and with
the advent of 64-bit CPUs, I suspect some of them will make a
comeback for a while.