The way you read a declaration in C is imagine you're evaluating an expression. So the example you give:
void(*signal(int, void (*)(int)))(int)
The result of the expression is void.
Parentheses go first in order of operations, so we evaluate this first:
(*signal(int, void (*)(int)))
Function call binds before dereference, so this goes first:
signal(int, void (*)(int))
"signal" becomes the actual declared name. At the bottom, we have a function that takes two parameters, an int, and a function pointer that takes an int and returns void. Now we work outwards.
This function returns a pointer, that when dereferenced, can be called with an int, and returns void when doing so.
In layman's terms, we have a function "signal" that takes an int and a function pointer and returns a function pointer, and both function pointers are to a function that takes an int and returns void.
Is it easy? If you can read C expressions, it can be easy. But it is definitely tedious, and does not come out quickly--it violates the principle that you have <type> <variable name>; it is not what I would consider readable or good design. And it would be easier if types were consistently read in one direction (like Rust types).