OK, the address itself isn't invalid, what is invalid is the assumption that j still exists. C and C++ pass arguments to functions by placing them on the stack, and local (automatic) variables are also created on the stack. So what your program does is to call fun() with no parameters; fun() creates j on the stack, then when it returns its stack frame and everything in it is discarded (although not necessarily changed). The main function then calls printf with a parameter "hi" which overwrites the memory that j previously occupied. Then you call printf a second time with two parameters which overwrite the memory that j previously occupied for the second time.
So that is why the program displays garbage for j. It's because you are assuming that j still exists, and that assumption is wrong. The problem is NOT the printf statement - both of those are perfectly fine.