5

Tengo un problema con el siguiente código

int main(void){

    char saludo [10]="Amigo";
    char* puntero = &saludo;
    printf("Nuestro puntero apunta a: %p \n",puntero);
    printf("La variable saludo esta en: %p \n",&saludo);
    if(&saludo == puntero){
        printf("Las direcciones apuntan al mismo lugar: %p \n",puntero);
    }
    *puntero = "Hola";//Modifico a donde apunta mi puntero
    printf("%s",saludo);
    return 0;

}

El problema que tengo es que después de compilar, lo que veo en consola no es lo que espero:

Nuestro puntero apunta a: 0xbfb47942 
La variable saludo esta en: 0xbfb47942 
Las direcciones apuntan al mismo lugar: 0xbfb47942 
�migo

Cuando lo que en realidad espero es una salida del tipo:

Nuestro puntero apunta a: 0xbfb47942 
La variable saludo esta en: 0xbfb47942 
Las direcciones apuntan al mismo lugar: 0xbfb47942 
Hola
David
  • 1,191
  • 1
  • 6
  • 13
IvFlores
  • 83
  • 5
  • Tu error de concepto es que una asignación como `a="cadena"` no copia esa cadena al "interior" de la variable `a`. Eso lo haría `strcpy(a, "cadena")`. Lo que hace en cambio es guardar el texto `"cadena"` en una dirección arbitrariad e memoria (en la zona de las cadenas estáticas o literales) y _asignar a `a` la dirección en que ha sido guardada. En tu caso estás cambiando el primer carácter de "Amigo" por el primer byte de esa dirección. Si hicieras en cambio `p="Hola"`, funcionaría bien, pero estarías cambiando la dirección a que apunta `p` – abulafia Sep 03 '19 at 10:19

2 Answers2

6

Parece que no tienes del todo claro cómo funcionan los operadores unarios1 * (des-referencia) y & (dirección de).

Contexto.

El operador de "des-referencia" o "contenido-de" sólo puede ser usado con punteros (o al declarar punteros) y obtiene el contenido de lo que sea que apunta el puntero:

int valor = 42;
int* puntero = &valor;
int** puntero_a_puntero = &puntero;
int*** puntero_a_puntero_a_puntero = &puntero_a_puntero;

printf("%d", ***puntero_a_puntero_a_puntero); // Muestra 42
printf("%d", *valor); // Error 'valor' no es un puntero

En el ejemplo anterior, tenemos una variable que es un puntero triple (apunta (1) a un puntero que apunta (2) a un puntero que apunta (3) a un valor), por eso si queremos el valor debemos des-referenciar tres veces.

El operador de "dirección de" se puede usar con cualquier variable y obtiene un puntero al tipo de la variable:

int valor = 42;
int* puntero = &valor;
int** puntero_a_puntero = &puntero;
int*** puntero_a_puntero_a_puntero = &puntero_a_puntero;

En el ejemplo anterior, la "dirección de valor" obtiene un puntero a entero (int*) porque valor es entero (int); mientras que la "dirección de puntero" obtiene un puntero a puntero a entero (int**) porque puntero es puntero a entero (int*) etc…

Problemas.

Una vez aclarado cómo funcionan los operadores unarios * y &, veamos qué uso les has dado en tu código y por qué el uso es incorrecto.

En estas declaraciones de variables:

char saludo [10]="Amigo";
char* puntero = &saludo;

El tipo de saludo es char[10] (formación2 de diez caracteres), el nombre de una formación es un puntero al primer elemento de la misma, por lo tanto: sabiendo que saludo es una formación de caracteres (char) el nombre de la formación es un puntero a carácter (char*).

Si obtenemos la dirección de la formación con el operador dirección-de, sabiendo que el nombre de la formación es un puntero a carácter (char*) el resultado será puntero a puntero a carácter (char**), ¡pero lo estás guardando en una variable de tipo puntero a carácter!

Sigamos:

*puntero = "Hola";

Sabemos que puntero es un puntero a carácter (char*) por lo que al des-referenciarlo obtenemos un carácter (char), este carácter será el primer elemento de la formación2 a la que apunta puntero.

Asignas al primer carácter de la formación a la que apunta puntero el literal de texto "Hola" que es de tipo char[5], y que es interpretado como puntero a carácter (char*); es decir: en un carácter (char) guardas un puntero a carácter (char*).

Los punteros se guardan en variables de 32 o 64 bits3 pero los caracteres son de 8 o 16 bits3 así que se trunca parte del valor del puntero para guardar lo que se pueda, lo cuál da lugar a un carácter no imprimible () cuando muestras la variable por pantalla.

Propuesta.

Si usases bien los operadores unarios * y & tu código debería parecerse a lo que ha mostrado SuperG280:

int main(void)
{
    char saludo [10]="Amigo";
    char* puntero = saludo; // SIN operador dirección-de

    printf("Nuestro puntero apunta a: %p \n",puntero);
    printf("La variable saludo esta en: %p \n",saludo);

    if(saludo == puntero) // SIN operador dirección-de
    {
        printf("Las direcciones apuntan al mismo lugar: %p \n",puntero);
    }

    puntero = "Hola"; // SIN operador de des-referencia
    printf("%s",saludo);
    return 0;
}

1El asterisco * se puede usar como operador de multiplicación (int i = 3 * 2;) o como operador de des-referencia (int v = *x;) el primero es el uso binario y el segundo es el uso unario. El et & se puede usar como operador de AND binario (int i = 7 & 3;) o como operador de dirección de (int *p = &x;) el primero es el uso binario y el segundo es el uso unario.

2También conocida como arreglo o en inglés array.

3Dependiendo de la arquitectura del sistema u otros factores.

PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82
5

Tienes varios problemas en este programa. Para empezar, tienes que pensar que el nombre de la variable definida como

char saludo[10] = "Amigo";

es la dirección de memoria del primer elemento del array. La variable saludo ya es una dirección de memoria, por eso cuando te quieres referir al contenido y no a la dirección de memoria, haces cosas como saludo[i], donde i es la posición dentro del array que quieres consultar, empezando en cero.

Dicho esto, si quieres un puntero al array, lo que tienes que hacer es:

char * puntero = saludo;

y no

char * puntero = &saludo;

es más, esto último en mi compilador ni siquiera compila. Tienes que quitar todos los & que has puesto delante de la variable saludo.

Así que tu programa quedaría así:

int main(void){
    char saludo [10]="Amigo";
    char* puntero = saludo;
    printf("Nuestro puntero apunta a: %p \n",puntero);
    printf("La variable saludo esta en: %p \n",saludo);
    if(saludo == puntero){
        printf("Las direcciones apuntan al mismo lugar: %p \n",puntero);
    }
    puntero = "Hola";//Modifico a donde apunta mi puntero
    printf("%s",saludo);
    return 0;
}

Pero esto no da el resultado que tú quieres. Cuando haces:

puntero = "Hola";

estás haciendo que puntero apunte a otro sitio, pero no alternas en nada la variable saludo, que sigue apuntando a la cadena "Amigo", así que cuando haces el último printf, obviamente te imprime el contenido de saludo: "Amigo" y no "Hola" como tu querías.

SuperG280
  • 1,889
  • 7
  • 14
  • El código del OP, compilar ... **compila**. Cierta cantidad de *warnings*, pero compila: https://wandbox.org/permlink/EyGvOUticMZUpsuw – Trauma Sep 03 '19 at 06:18
  • Con Microsoft Visual Studio esa línea no compila y da un error: error C2440: 'initializing' : cannot convert from 'char (*__w64 )[10]' to 'char *' – SuperG280 Sep 03 '19 at 06:21
  • El código mostrado por el OP es perfectamente compilable, siempre y cuando el compilador use cadenas literales `utf-8`. Como ya habrás observado por el mensaje de error que indicas, tu compilador no lo hace. – Trauma Sep 03 '19 at 06:25
  • Efectivamente, las cosas de Microsoft son como son... Y los estándares se los pasa por donde se los pasa... – SuperG280 Sep 03 '19 at 06:30