[on omitting identifiers in some situations]
The thing being declared here is the function foo(),
and its identifier is present and accounted for. The function
parameters are not declared, and would not be declared even
if you gave names for them. The names, if present, are really
just documentation; all that matters is their number and types.
Right (of course

).
Moving the question back a bit, to "why do function prototypes
have optional identifiers in the prototype-scope portion of the
declaration between the parentheses", the answer is: "historical
reasons". (This phrase is sometimes modified into "hysterical
raisins", which might make some think of some classic Claymation:
<
>)
K&R-1 C did not have function prototypes. To declare function
foo, you wrote:
int foo(); /* K&R-1 C also lacked "void" */
which was your directive saying: "hey, Mr Compiler, the name foo
refers to a function with return-type int". You never told the
compiler how many arguments this function would take, or what their
types would be. You never even had the option. The compiler would
just assume that you got all the calls to foo() right, using casts
if needed to get argument types correct.
C++ came along, and changed all this. In every function declaration,
you *had* to tell the compiler the number of arguments and their
types. These were "function prototypes". It was a clear improvement,
since people did in fact get arguments wrong sometimes, sometimes
producing hard-to-debug runtime problems.
C++ was done by a guy with less good taste than Dennis

and as
a result, the syntax invented for C++ tends to be klunkier than
that for C (not that C's is "non-klunky" in various cases in the
first place -- and to be fair to Bjarne, modifying C while staying
reasonably backward-compatible with it is difficult in the first
place). So he made the identifiers optional.
The C Standards group came along and stole prototypes wholesale
from C++, optional identifiers and all. They did make one more
change, to make the syntax even klunkier: in order to maintain
somewhat more backward compatibility with K&R-1 C, they decreed
that empty parentheses would not mean "no arguments", but rather:
"I, the programmer, refuse to tell you, Mr Compiler, how many
arguments and what their types are." So if you *wanted* to tell
Mr Compiler that there should be no arguments, you had to write:
int func(void);
and not the more-obvious:
int func();
(even though in C++, both of these mean "no arguments").