P1 might cause issues.
int x = 10;
/* "x" is a local variable.
The lifetime of x is only until the execution of g().
So, we're returning an address which will soon expire. It screams runtime error.
P2 might cause issues.
/* px is a local pointer variable.
The value of a local variable, if not initialized, is garbage value.
Hence, the content of px is currently garbage*/
*px = 10;
/* Here, we're trying to access the garbage contents of px. So, Runtime error. */
P3 might cause issues.
/* Local pointer variable px. Currently, it's content is garbage value, because not initialized yet. */
px = (int*) malloc (sizeof(int));
/* Now, we initialized px, hence it's content isn't garbage anymore.
What we did is, assigned a block of memory from heap. In case the heap is full,
initialization fails. So, in the worst case, contents of px remain garbage value */
*px = 10;
/* Trying to access garbage value. Runtime error. */
Correct Answer, Option D.
But if we focus on the word "likely" in the question, then almost all modern computing systems use Virtual Memory, which provides an illusion of a huge amount of addressing space to each process. Hence, they see an almost unlimited heap at their disposal. So, malloc() is extremely unlikely to return NULL. So, the answer could be Option C, too — and I'm more inclined to option C.