why C type syntax is that way

C is the sequel to B which was entirely typeless, i.e. there was only one type, the machine word. code looked like this1:

printn(n,b) {
    extrn putchar;
    auto a;

    if(a=n/b) /* assignment, not test for equality */
        printn(a, b); /* recursive */
    putchar(n%b + '0');
}
there are, as a result, no type declarations. local variables are declared with auto and global variables (in every function they’re used) are declared with extrn.

at a certain point B was ported from a word-addressed machine, the PDP-7, to a byte-addressed machine, the PDP-11. there could no longer be a single type, since there were multiple types even at an architectural level. B did still run with some clever hacks. (the compiler was programmed to insert a little startup routine which would fixup addresses (as filled in by the linker) to be word addressed!)2

the B language would evolve into a language with multiple types. and one might see how this would be most naturally done. there are already statements to declare certain kinds of variables, so as one could write auto a or extrn b, one may as readily write char and by extension int.

of course, then, there are also pointer types. with int a declaring that a is an int, then why couldn’t int *a mean that *a is an int? it’s quite simple.

the syntax for all the other types all follow this logic. given int (*a)(), (*a)() is an int. however, with all of the different types, this quickly becomes confusing, which is the problem with it. but I will dispute any notion that it is completely illogical, or even that bad. it is entirely excusable (except that typedef causes it to be somewhat absurd because it makes the grammar context-sensitive). and this, by the way, is why you should never, ever write pointer types like this:

int* inexcusable;
if you do this, I will burn down your house!

P.S. the subject of C++ is completely irrelevant to the discussion of the C programming language.