To print out pointer values (NULL or otherwise), you must use the %p conversion specifier, which expects its corresponding argument to have type void *:
printf("p = %p\n", (void *) p );
This is probably the only time in C you need to explicitly cast a pointer value to void * (if it isn't already void *, anyway). The reason why is a combination of how printf reads its arguments, default type promotions in variadic functions, and a couple of other things that I have yet to figure out how to explain well.
Note that you are casting the value of p (the pointer), not the thing that it points to.
In the second print statement,
printf("*p = %d\n", *p );
you are printing the value of the thing that p points to, which is an integer. The expression *p has type int, which is what the %d conversion specifier expects.