Los punteros pueden ser constantes. El compilador seguirá intentando evitar el almacenamiento y hacer plegado de constantes cuando se trata de punteros constantes, pero estas características parecen menos útiles en este caso. Lo más importante es que el compilador le avisará si intenta cambiar un puntero constante, lo que representa un buen elemento de seguridad. Cuando se usa const con punteros tiene dos opciones: const se puede aplicar a aquello a lo que apunta el puntero o a la propia dirección almacenada en el puntero. La sintaxis es un poco confusa al principio pero se vuelve cómoda con la práctica.
El truco con la definición de un puntero, al igual que con cualquier definición complicada, es leerla empezando por el identificador e ir analizando la definición hacia afuera. El especificador const está ligado a la cosa «más cercana». Así que si quiere impedir cambios en el elemento apuntado, debe escribir una definición parecida a ésta:
const int* u;
Empezando por el identificador, se lee «u es un puntero, que apunta a un entero constante». En este caso no se requiere inicialización porque está diciendo que u puede apuntar a cualquier cosa (es decir, no es constante), pero la cosa a la que apunta no puede cambiar.
Ahora viene la parte algo confusa. Podría pensar que hacer el puntero inalterable en sí mismo, es decir, impedir cualquier cambio en la dirección que contiene u, es tan simple como mover la palabra const al otro lado de la palabra int:
int const* v;
No es tan descabellado pensar que esto debería leerse «v es un puntero constante a un entero». Sin embargo, la forma de leerlo es «v es un puntero ordinario a un entero que es constante». Es decir, la palabra const se refiere de nuevo al int y el efecto es el mismo que en la definición previa. El hecho de que estas definiciones sean equivalentes es lo confuso; para evitar esta confusión por parte del lector del código, probablemente debería ceñirse a la primera forma.