C is completely easy to parse, because it reduces to a LALR(1) parseable context-free language after the semantic mapping of input symbols into abstract lexical categories. In fact, it can be parsed by recursive descent with a suitable factoring, so that it is almost certainly LL(k), possibly with k = 1. Pardon me, k == 1. :)
That mapping requires semantic information (type declarations), but that information is something the compiler must maintain anyway so that it can type check the language.
Yes, if we simply treat "foo" as an IDENTIFIER token and feed that to the parser, thing swill be difficult. But we don't; what we return to the parser is a TYPESPEC token, based on peering into the current parse-time lexical environment where we resolve "foo" to a typedef name. The parser then quite easily goes through the state transitions corresponding to its declaration rule.
Sure, a parser which maintains parse-time lexical environments which are also visible to the lexer is slightly harder to write than one which doesn't have such stuff (and just returns an unadorned AST which you can walk again to build that info).
But if the year is 1973, you would likely do that anyway even if the language doesn't require it. Why? Because building the compile-time environment tree reduces the number of passes: from a single pass through the raw code, you already have the syntax tree with type info (and possibly type info that has even been checked).