First of all, your code is not very modern C++. It's basically "c with iostreams".
Second of all, printLoop(int p[2][3]) is the signature you're looking for even though again, it's not the best way of doing things at all.
Third of all, int (*p)[3] is analyzed as follows: Start at the name which is p and take a look around (first to the right and then to the left yet here it doesn't matter) until you "hit" braces. There's only a star at it, so you can say that p is a pointer. Now you recursively do the same analysis again, you see [3], which means that p is a pointer to an array that has 3 ints.
Now I'd like to mention the following:
Use std::array for staticly-sized arrays.
Use std::vector for dynamicaly-sized arrays.
Oh, also, I myself wouldn't use a 2D array, they are clunky and just a syntactic sugar (around the basic "array" notion which is a syntactic sugar as well).
So perhaps, something like this, brain compiled, hopefully correct, C++11 abusing:
std::array<int, 3 * 2> p = {{1, 2, 3, 4, 5, 6}};
std::for_each(std::begin(p), std::end(p), [](int elem){ std::cout<<elem; });
Nice and dandy. You could also have lambda check for some "2d array" sizes and insert newlines if you so desire.
pis a pointer*to an array[]of3ints.