What is the difference between
int a1[9];
and
int a2[3][3];
(I've changed the names so I can talk about the declarations more easily.)
The difference is that they're of different types. They both have the same underlying memory layout,each consisting of a contiguous region of memory 9 times the size of an int. But a1 is an array of 9 int objects, while a2 is an array of 3 objects each of which is an array of 3 int objects. It's a multidimensional array, which in C is precisely an array of array, nothing more, nothing less.
The difference is not just syntactic sugar. You might get the same generated code for certain operations, for example a1[1] and a2[0][1]. But, for example, a1[3] refers to the 4th element of the array a1, while a2[0][3], though you might think it refers to the same thing, actually has undefined behavior. (Compilers are permitted, but not required, to perform run-time array bound checking, and are permitted to assume that array references do not go past the end of the indexed array object.)
printf("%d\n", a2[1]);
As others have mentioned, this has undefined behavior. a2[1] is an array object of type int[3]. An expression of array type is, in most contexts, converted to an expression of pointer type, so a2[1] ends up being of type int*, and yields a pointer to the initial element of the second row of a2. To print a pointer value, use %p -- which requires an argument of type void*, so you need to cast it:
printf("%p\n", a2[1]);
Recommended reading: Section 6 of the comp.lang.c FAQ.