Direcciones de literales cadena

Versión en PDF

Considérese el siguiente código:

#include <stdio.h>

int main() {
    char *s="hola";
    // Recuerda que %p formatea como dirección de memoria
    printf("%p\n",&s);
    printf("%p\n",s);
    printf("%p\n",&"hola");

    return 0;
}

¿Cuál de las siguientes es cierta?

  • El programa imprime la misma dirección de memoria tres veces.
  • El programa imprime tres direcciones de memoria, pero no son las tres iguales.
  • El programa da un error de ejecución porque se está guardando en el puntero un valor sin haber reservado espacio.
  • El programa no compila porque &"hola" es ilegal, ya que se aplica el operador dirección de a un literal.

La respuesta correcta es la segunda. En la primera línea del main creamos un puntero a char y guardamos en él la dirección de memoria donde hay un literal cadena "hola", todo correcto. También es legal la operación &"hola", que recuperará la dirección de memoria del programa donde está guardado el literal. Paradójicamente, un literal cadena es un lvalue, a pesar de que no pueden ir al lado izquierdo de una asignación. Respecto a las direcciones, está claro que &s y s son diferentes, porque la primera es la dirección del puntero y la segunda su contenido, la dirección de la cadena "hola". Con esto hemos resuelto el test.

Queda una cuestión que no nos pedía distinguir las opciones del test: ¿coincidirán s y &"hola"? La respuesta más correcta es que depende. La descripción del estándar de C no dice sobre los arrays en que se almancenan los literales cadena que «It is unspecified whether these arrays are distinct provided their elements have the appropriate values» (C11 6.4.5/7). Es decir, que queda en manos del compilador. Lo más cabal sería que, sobre todo en este escenario tan sencillo, lo reconozca y haga que estas dos direcciones sí coincidan. En todas las pruebas que he hecho con gcc y clang ha sido así.

Una observación interesante es que la direcciones de memoria que observamos no serán cercanas. El literal &"hola" se almacena en el segmento de datos, mientras que el puntero s estará en la pila (stack). Si en su lugar hubiéramos usado una variable global, esta también se hubiera ubicado en el segmento de datos.