consulta sobre el uso del ampersand en variables de tipo string

Iniciado por etcheverrypablol, 18 Enero 2016, 19:38 PM

0 Miembros y 2 Visitantes están viendo este tema.

etcheverrypablol

Hola, aparezco por aquí con otra duda. Este programa:

#include <stdio.h>

int main(int argc, char const *argv[])
{
   char pax[]="Juan Sin Miedo";
   printf("%s %s\n",pax,&pax[4]);
   puts(pax);
   puts(&pax[4]);
   return 0;
}

lo que hace es mostar por pantalla lo siguiente:

Juan Sin Miedo  Sin Miedo
Juan Sin Miedo
Sin Miedo

No entiendo, qué es lo que hace la sentencia puts(&pax[4]);.
Por lo que veo comienza a mostrar la cadena desde la posición 4. Pero no sé por qué se hace uso del ampersand ni cómo es que funciona este operador en este caso.

Alguno de ustedes me puede explicar por favor. Muchas gracias.

MAFUS

& devuelve la dirección de memoria de la variable que le sigue.
%s de printf y puts esperan la dirección de memoria del inicio de una cadena. Un puntero es una dirección de memoria y el nombre de un array también actúa como un puntero.
En cambio cuándo haces pax[4] estás accediendo a una variable de tipo char, no a una dirección. Así para que la opción %s de scanf y puts escriban la cadena a partir de pax[4] hasta el final debes pasárselo como dirección de memoria y eso se hace con el & delante.

Cuando llegues a los punteros sabrás más de ello.

etcheverrypablol

Muchas gracias por responder MAFUS! Estoy muy agradecido :)
Ahora lo entiendo. He visto ya sobre punteros en pascal, y un poco en c, y no me daba cuenta.
Ahora una duda, vos dijiste que %s de printf y puts esperan la dirección de memoria del inicio de una cadena. Eso, no es siempre así, ¿o si?. Porque cuando le pasas una cadena solamente, como el caso de pax, no le pasas la dirección.

MAFUS

Sí, pax, en este caso, es un array y por tanto cuándo se acceder a la variable solo con su nombre estás accediendo a la dirección el primer elemento de este array.
Como prueba de ello puedes probar lo siguiente:
/* Después de char pax[]="Juan Sin Miedo"; */
char *puntero_a_char = pax;
printf("%s %s\n",puntero_a_char, &puntero_a_char[4]);
puts(puntero_a_char);
puts(&puntero_a_char[4]);

Yoel Alejandro

Cita de: etcheverrypablol en 18 Enero 2016, 20:12 PM
Porque cuando le pasas una cadena solamente, como el caso de pax, no le pasas la dirección.

Recuerda que en C las cadenas son arreglos de datos char, y como en todo arreglo en C, el nombre del arreglo es un apuntador al primer elemento del mismo. En otras palabras, el nombre de la cadena es realmente una variable tipo char* (puntero a char) que contiene la dirección de memoria del primer carácter de la misma.


char str[] = "Hola mundo";

printf( "contenido de str: %s\n", str );
printf( "pero str es un puntero cuyo valor es: %p\n", str );
printf( "y apunta al primer elemento el cual es --> '%c'\n", *str );


Salida:

    contenido de str: Hola mundo
    pero str es un puntero cuyo valor es: 0xbfcbe9b5
    y apunta al primer elemento el cual es --> 'H'


Saludos, Yoel.

Saludos, Yoel.
P.D..-   Para mayores dudas, puedes enviarme un mensaje personal (M.P.)

D4RIO

Excelente respuesta la de Yoel, muy completa.

Agrego algo más técnico, por si interesa:

Si descendemos al nivel de la implementación, las llamadas a funciones de C meten los parámetros en la pila (push). La función sabe cuánto debe sacar de la pila para ser usado como parámetro (exceptuamos acá las funciones variádicas), esto porque le dijimos al compilador de C el tipo de cada parámetro y, con ello, su tamaño.

Imagina ahora que le pasamos str, que es un char[]. Si pasáramos toda la cadena por 'valor' a la función, cada cadena podría meter una cantidad de carácteres diferente a la pila, para lo cual la función debería sacar carácter por carácter de la pila, verificar que no sea un '\0' y seguir. No es que sea imposible, pero es algo sucio y que no sigue la línea de comportamiento de las llamadas donde se pasa un vector cualquiera, digamos, de enteros, que se pasan por referencia. La función ciertamente NO tiene idea de cómo saber dónde terminar de procesar un vector de enteros (se podría, pasando el tamaño, pero eso ensucia más la convención, y queremos mantener las cosas simples).

Por eso dentro de la convención de llamadas a función de C se eligió la forma más sencilla y comprensible posible: Cualquier cosa que sea un vector (blabla[]) se pasa por referencia a las funciones, esto es, se pasa un puntero. De esa forma las funciones saben que deben sacar de la pila un puntero, que es siempre del mismo tamaño, tamaño dirección de memoria del sistema.

En las funciones variádicas nosotros podemos sacar parámetros de la pila como nos de la gana, y eso nos permitiría pasar un string por valor. Sin embargo, cuando pases un char[], notarás que NO se mete la cadena en la pila del sistema, aunque uses varargs la cadena simplemente no está ahí, y esto es porque cuando el compilador de C lee que pasa un char[], mete el puntero a la pila. Está programado para hacer eso.

Saludos, espero haber agregado conocimiento y no dudas.
OpenBSDFreeBSD

etcheverrypablol

#6
Hola Yoel, tenía conocimiento de eso. Lo que no sabía es que exitía el código de formato %p que te permite mostrar la dirección de memoria de un puntero. La verdad chicos que me sirven muchisimo sus respuestas. Gracias! :)




Hola D4RIO, te aseguro que si tengo más dudas jajaja. Es muy interesante la explicación que diste. Me gusta saber cosas de ese nivel. Aun me falta mucho por aprender y hay algunas cosas que no entendí de las que dijiste. Pero muy pronto lo haré. Gracias por su tiempo y dedicación chicos.