Let's assume that realcallback has no use other than to be a callback function. Then, this suggestion is not much better than simply having realcallback use global_ptr directly.
Now say you wanted to have one invocation of realcallback with one argument, and another invocation with another. Then it makes sense for realcallback to exist - but you would still need two callback functions (and two globals). And if you needed three different invocations, you'd need three, and so on. This is all easy enough to do, but not extensible in any data-driven fashion.
The hack generates a separate thunk for each value, but at runtime, so you have as many callbacks as you have context arguments, and without needing to write them by hand ahead of time.
(Fixing the calling code is a much better solution, where available. You virtually always need a context parameter, and when you don't, it's easy enough to ignore. "Solving" the problem with this sort of ugly hack is just making more work for yourself and for others.)
For multiple functions taking different arguments you could exploit atexit's specified calling order
data_t *global;
int main() {
atexit(callback);
atexit(set_global_to_x);
atexit(callback);
atexit(set_global_to_y);
}
Since functions are called in reverse order of their installation using atexit, you end up with two calls to callback; one with global set to y and one with global set to x.
It might be more useful in cases where you want to have multiple callbacks, and don't know how many at compile time. I would still try and avoid it, though.
(And aside, I would make global_ptr static. That makes it invisible from outside its compilation unit. Even more pedantic: callback should be a void function)