NULL should be defined as ((void * )0) for C always (and you should define your own NULL to enforce that if necessary in varargs functions). This is safe because C allows conversion from void * to other pointer types.
C++ does not allow conversion from void * to other pointer types, so what you were supposed to do was use plain 0, which introduced exactly the issue you mention (and which has bitten me before trying to use a C library from C++, where NULL was defined as plain 0).
With C++11 you should use nullptr, which does the right thing. Some implementations define NULL in C++ to a vendor-provided symbol which emulates nullptr so it might be safe to use NULL. If you have C++98 only and don't want to risk NULL then you should use static_cast<T*>(0) (where T is the actual pointer type), which is admittedly clunky.
So the bug you mention which bit you by using "C library from C++" was not caused by a difference between C and C++. It was caused by a difference in the definition of NULL between those two compilers. You could just as easily have found a C++ compiler which did work, or a C compiler which didn't.
class Foo { };
int main() {
Foo *foo = malloc(sizeof(Foo)); // returns void*
}
In the C Standard, an implementation can define NULL as either 0 or (void * ) 0. For an example of how that can go terribly wrong, consider the following:
on an architecture where sizeof (int) != sizeof (ptr)