Q:

Why must I provide a dimension when passing a two-dimensional array to a C function?

I'm not sure if the history tag is relevant, but feel free to add it.
I would presume the reason is historical, which is why I suggest this.
Why is it that I cannot declare a function signature such as the following?
void foo(int doubly_indexed_array[][]) {
  ...
}

which gives
$ gcc mem.c
mem.c:4: error: array type has incomplete element type

Why must you declare one of the dimensions as in the following?
void foo(int doubly_indexed_array[][10]) {
  ...
}

A:

You need to declare the second one and not only one. It has to do with memory layout, a 2-d array is stored contiguously in memory which means all the second dimension arrays are contigous.
So for int[2][2] the memory layout looks like(assuming initialization to 0):
[[0, 0][0, 0]]

Compiler has to know by how much to increment the pointer when indexing on the first dimension for example. So if an int array is named a, 
a[i][j] is really (address of a) + i*sizeof(int)*second_dimension + j*sizeof(int)
All of this need to be known at compile time so the compiler can generate code.

