I can see two distinct questions here.
First, on modern architectures it's pretty safe to assume that pointers are the same size (that is, there are no near/far pointers; but pointers to member functions aren't regular pointers and may be a different size); and on a 32 bit system that size is generally 32 bits. C even goes so far as to automatically cast void*
to anything else because, after all, a pointer is really just a memory address. However the language definitions distinguish various pointers as different types. I believe the reason for this is that different types can have different alignment (the void*
rule is that nothing can really be of type void
, so if you have a pointer to void
you likely know the correct type and, implicitly, the correct alignment(see note))
Second, as others have pointed out, long
s and ints
are fundamentally different types with default conversions built in. The standards require long
s to be at least as large as int
s, but possibly larger. On your architecture, the alignment is probably the same but on other architectures that could be different as well.
It is possible to fudge in your case, but it's not portable. If you do not #include
the correct function declarations, and instead simply forward declare them yourself things should magically work because in your case long
s and int
s are compatible (assuming no signedness issues; also in C++ you'll need to extern "C"
both your declarations and the actual function implementations so that you don't get link errors). Until you switch to a different compiler, or different operating system, or different architecture, etc.
For instance, in C++ you could do this:
// in file lib.cc
#include <iostream>
extern "C" void foo_int(int* a)
{
std::cout << "foo_int " << *a << " at address " << a <<'\n';
}
extern "C" void foo_long(long* a)
{
std::cout << "foo_long " << *a << " at address " << a <<'\n';
}
// In file main.cc
extern "C" void foo_int(long* a);
extern "C" void foo_long(int* a);
int main()
{
int i = 5;
long l = 10;
foo_long(&i);
foo_int(&l);
}
(In C, you would get rid of the extern "C"
and use printf
instead of cout
).
Using GCC you would compile like so:
$ g++ -c lib.cc -o lib.o
$ g++ main.cc lib.o
$ ./a.out
foo_long 5 at address 0x22cce4
foo_int 10 at address 0x22cce0
NOTE Since there are no objects of type void
, a void*
can only point to objects of some other type. And the compiler knew that real type when it put the object there. And the compiler knew the alignment for that type when it allocated the object. You may not really know the alignment, but the cast is only guaranteed to work if it's back to the original type, in which case the alignment and size will be the same.
But there are wrinkles. For one, the object's packing must be the same in both places (not an issue with primitive types). For another, it is possible to point at arbitrary memory with void*
s, but programmers who do that presumably realize what they're doing.