An array of arrays like yours look like this in memory
+---------+---------+---------+---------+---------+---------+ | x[0][0] | x[0][1] | x[0][2] | x[1][0] | x[1][1] | x[1][2] | +---------+---------+---------+---------+---------+---------+
The location of x, x[0] and x[0][0] is all the same.
Also, arrays naturally decays to pointers to their first element. If you use plain x you will get &x[0]. If you use x[0] you will get &x[0][0]. So when you do *x[0] it is the same as doing *&x[0][0], and the dereference and address-of operators cancel each other out so you are left with x[0][0], which is the value you print.
Furthermore, to help you understand why x is the same as &x[0], you need to know that for any array or pointer x and index i the expressions x[i] is the same as *(x + i). That means the expression &x[i] is the same as &*(x + i), and since the address-of and dereference operators again cancel each other out &x[i] is the same as (x + i) (or without the parentheses x + i). Now think of the case when i is zero, then we have &x[0] which is the same as x + 0 which is the same as x. So &x[0] is the same as x, and vice-versa.
For others that wonders what &x and &x[0] and &x[0][0] are representing, please see this:
+---------+---------+---------+---------+---------+---------+ | x[0][0] | x[0][1] | x[0][2] | x[1][0] | x[1][1] | x[1][2] | +---------+---------+---------+---------+---------+---------+ ^ ^ ^ ^ | | | | +- &x +- &x[0][1] +- &x+1 +- &x[1][1] | | +- &x[0] +- &x[1] | | +- &x[0][0] +- &x[1][0]
While all of x, x[0], &x, &x[0] and &x[0][0] may represent the same memory address, they are semantically different, i.e they represent different types:
xis a pointer to an array of threechar(orchar (*)[3])x[0]is a pointer tochar(orchar *)&xis a pointer to an array of arrays of threechar(orchar (*)[3][3])&x[0]is a pointer to an array of threechar(orchar (*)[3])&x[0][0]is a pointer tochar(orchar *)
5
solved A short C++ program about the pointer