In C, errors don't trickle down and you need to deal with them in each level of abstraction, which can be totally useless and time consuming.
Time consuming, yes, absolutely. "Useless" I don't understand at all. It's structured exceptions that more often seem useless to me.
The more layers of code an exception bubbles up (or trickles down) through, the less the exception handler can know about where it happened, why it happened, or what the resulting state of the program is. Very often, the only "handling" that can be done is making a report of the exception.
It seems to me that the most useful exception handlers, the ones most likely to actually salvage the situation and allow the program to continue to work, are the ones that immediately follow an exception-throwing call, the ones that don't allow any trickle-down.
But those are degenerates, of course. They're functionally equivalent to C-style error return codes.
One problem with C-style error handling is that having error handling at all levels makes it impossible to reuse code without tweaking it. Say you handle an error by printing a warning message to stderr, now you can't reuse that code in anything that doesn't want error messages printed there (maybe it doesn't want errors printed, or it needs to localize them, or it's using stderr for something else like in strace). So error handling in C kill reusability.
Another problem with C-style error handling is that the number of error types increases as you pass the buck up levels, but C doesn't have any good way to express more than one type at a time. Say one function returns true or false and another returns an error constant like errno. When one function calls the other, what do you return at the top level? You could map one to the other, but you've lost maybe important information about the error. So error handling in C kills composability.
And a third problem is documentation. With no standard error types that are known to the compiler it is rarely possible to tell the caller they forgot some error handling.
Exceptions address the first two problems, and checked exceptions the third one. A common misunderstanding of Java checked exceptions is that they force the caller to handle the error, when what they really do is force to caller to document the errors it can generate.
Also, setjmp: http://en.wikipedia.org/wiki/Setjmp.h if you really really really like exceptions and want them in C.
The declared exceptions (and exceptions vs errors) in Java are a nuisance, IMHO. For example, use any kind of "dependency injection" ("strategy" pattern, driver plugin(s) for other, older languages) via reflection, and suddenly all of your exceptions have transmogrified into errors :-(