- Pernicious overallocation: When allocating memory, you either have to have complicated code to grow buffers on the fly, or allocate as much as you could ever possibly need up front. In C, the latter is almost always chosen, for simplicity's sake. This leads to programs taking up more memory than they really need to.
- Terrible string support: Or to be more precise, nonexistent. C strings have two major problems. First, null-terminated strings are slow - to get the length of a string, you have to read through the entire thing! They also makes it unreasonably difficult to apply certain loop optimizations to strings. Second, having strings as plain arrays means that you can't make immutable strings, which in turn means that every time you pass a string to code you don't control, the safe thing to do is make a copy of it. (See 1.)
- Pointers: In a nutshell, the problem here is that it's difficult (maybe impossible?) for the compiler to get complete data about what pointers go with what memory, and that hides a lot of optimizations from the compiler. A lot of work has been put into pointer analysis, and they've accomplished some pretty amazing stuff, but at the end of the day, it's still not a solved problem.
There's more I want to write about this, but I also kind of want to sleep. >_>