Convertir un puntero no 'vacío' a `uintptr_t` y viceversa

Hay dos reglas estándar de C relacionadas:

Estándar C99,6.3.2.3:

Un puntero a anular puede convertirse ao de un puntero a cualquier tipo de objeto o incompleto. Un puntero a cualquier tipo de objeto o incompleto puede convertirse en un puntero para anular y volver de nuevo; el resultado se comparará igual al puntero original.

Y7.20.1.4:

El siguiente tipo designa un tipo entero sin signo con la propiedad de que cualquier puntero válido para anular se puede convertir a este tipo, luego volver a convertir a puntero a anular, y el resultado se comparará igual al puntero original:uintptr_t

Significa que el siguiente código es compatible:

int *p = NULL;
void *q = (void*)p;
uintptr_t s = (uintptr_t)q;

¿Pero realmente necesita el elenco de dos pasos? ¿El compilador realizará una conversión intermedia implícita si hace algo como:

int *p = NULL;
uintptr_t s = (uintptr_t)p;

(Bueno, probablemente lo hará en la mayoría de los compiladores, pero mi pregunta es sobre el cumplimiento estándar)

Respuestas a la pregunta(2)

Su respuesta a la pregunta