En los ciclos cada quien los hace a su manera mi condicional es que sea menor que tres asi que esta bien.
what??? no... si tienes menos de X elementos no es como tu quieras... es menos de x elementos... lo tenías mal...
sobre el codigo...
tienes la declaración mal... si son int, son int, si son float, son float... pero no ambas (y no cada quien no lo hace a su manera, es como el compilador lo acepte y siguiendo los standards)
int arreglo[3][3] no es un puntero a un espacio de memoria abierto... es un puntero a secciones fijas de 3 espacios (en la segunda dimensión) el es equivalente a int [][3]
para pasarlo como parámetro tienes 2 opciones...
opcion 1:
cambias la declaración de la función para que acepte punteros a secciones de 3 espacios de memoria continuos...
void imprimir (int p[][3]);
opción 2:
lo haces realmente un puntero
int **arreglo;
arreglo = new *int[3].;
// todo lo que venga