They suck for expressiveness and safety because there's no way to define a self referential type in C (a function can't return a pointer to a function of the same type). The only workarounds for this are kludges, often involving some form of type erasure (void pointers).
They also suck for performance because they're huge (who needs a value that can cover the full address space - almost nobody; you're just wasting precious cache) and make branch prediction harder than it needs to be (shared code that dererferences externally furnished function pointers can land nearly anywhere, every time).
The only thing they're good for is to save the programmer when they're backed themselves into a corner and need a heaping helping of dynamic binding to get out. Useful, sure; but not the first or best choice.