В первом фрагменте вы передаете двойной указатель по значению. Поэтому, когда вы вызываете mem_alloc2D(p, Nx, Ny)
указатель p
, который вы передаете, не «обновляется» при выходе из функции. Передача указателя по ссылке
void mem_alloc2D(double** &U, unsigned Nx, unsigned Ny)
должно заставить его работать.
Во втором фрагменте вы возвращаете только что созданный указатель, поэтому он работает нормально.