[Opinión] Interesante manera de usar char* con cout

Iniciado por Shout, 23 Julio 2013, 13:35 PM

0 Miembros y 1 Visitante están viendo este tema.

eferion

Perdona, no me dí cuenta de que eras tu... es lo que tiene estar a mil cosas a la vez :)

zonahurbana

Cita de: eferion en 24 Julio 2013, 07:11 AM
Si, es cierto, pero la función que recibe un vector de caracteres no se limita a imprimir ese carácter ... la función que recibe un vector de caracteres va a procesar todos los caracteres del vector hasta que encuentre un carácter nulo.
Me estaba refiriendo a lo que decía el autor del tema.

Entonces he escrito lo siguiente:
Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;

void something(char *text)
{
    cout << *text;
}

int main()
{
    char v[]="Probando";
    cout<<&v<<endl;
    cout<<v<<endl;
    something(v);
    return 0;
}

Y pues, yo creí que pasar como argumento v es lo mismo que pasar &v (pero no es exactamente lo mismo).
Lo primero muestra la dirección de memoria del primer elemento.
Lo segundo imprime toda la cadena como dices.
Pero yo me refería a lo tercero, que sólo muestra el primer caracter (es por el operador de indirección).

Si escribo lo siguiente, sigue mostrando solo la letra 'P':
Código (cpp) [Seleccionar]

    char* p = v;
    cout<<*p;


Eso quiere decir que cuando a cout le paso v o &v[0] es lo mismo... o incluso si le paso p, pero escribir *p ya le quita la gracia porque muestra el valor del elemento correspondiente a la dirección de memoria (es decir, sólo v[0]).

Entonces podemos decir que v es como un puntero constante a char.
Cuando cout recibe un puntero a char (sea constante o no), muestra el valor apuntado por ese puntero y los valores ubicados en las direcciones de memoria que le siguen, hasta encontrar la marca de fin de cadena.
Pero al escribir &v es como querer obtener la dirección de memoria de algo que ya propiamente es una dirección de memoria y se muestra entonces la dirección de memoria de v[0]. ¿Sería la única forma de mostrar la dirección de memoria de v usando cout?
Si muestro por pantalla &p (donde p es una variable puntero que almacena la dirección de memoria de v[0]) debería pasar lo mismo (pues estoy tratando de obtener la dirección de memoria de una dirección de memoria). Pero al probar esto, la dirección que se muestra es la dirección de la variable puntero, y no se está desreferenciando una dirección de memoria, que es lo que aproximadamente sucede al hacer &v (creo que me estoy haciendo mucho lío en vano :silbar:).

No se me permite hacer algo como:
Código (cpp) [Seleccionar]
    p = &v;
Pero sí esto:
Código (cpp) [Seleccionar]
    p = &v[0];
    cout<<*p;

Y el resultado nuevamente es 'P'.

Cita de: eferion en 24 Julio 2013, 07:11 AMA ver, cout es una clase de c++. Esta clase tiene varias sobrecargas del operador de inserción ( << ), una para cada tipo de dato soportado.
En realidad cout es un objeto de la clase ostream (o eso es lo que me dice esta referencia).


Y respecto a:
Cita de: eferion en 24 Julio 2013, 07:11 AM
Código (cpp) [Seleccionar]
void something(char *text){
    cout<<&(*(&(*(&(*text)))))<<endl;
    cout<<text;
}

En ambos casos te va a mostrar una cadena de texto. A la función que recibe el dato únicamente sabe qué tipo le estás pasando, no entiende de las transformaciones previas que le hayas hecho.
Es porque las transformaciones se hacen previamente y el resultado de dicha expresión es lo que se envía como argumento a cout (ya resuelto), pero eso no implica que yo deba ignorar dichas transformaciones.
Es decir, en este caso se referencia y desreferencia una cantidad igual de veces y luego de hacer todo ello recién muestra lo correspondiente. No es que deba ignorar las transformaciones y fijarme sólo en el tipo de dato que le envío... es decir, si escribiera un & o un * menos, el resultado no sería el mismo (aún cuando las conversiones se estén haciendo sobre un tipo de dato char*).
Nunca dejar de aprender es importante, más allá del ritmo que se siga ...

rir3760

Cita de: zonahurbana en 24 Julio 2013, 22:58 PMEntonces podemos decir que v es como un puntero constante a char.
Cuando cout recibe un puntero a char (sea constante o no), muestra el valor apuntado por ese puntero y los valores ubicados en las direcciones de memoria que le siguen, hasta encontrar la marca de fin de cadena.

Pero al escribir &v es como querer obtener la dirección de memoria de algo que ya propiamente es una dirección de memoria y se muestra entonces la dirección de memoria de v[0]. ¿Sería la única forma de mostrar la dirección de memoria de v usando cout?

Si muestro por pantalla &p (donde p es una variable puntero que almacena la dirección de memoria de v[0]) debería pasar lo mismo (pues estoy tratando de obtener la dirección de memoria de una dirección de memoria). Pero al probar esto, la dirección que se muestra es la dirección de la variable puntero, y no se está desreferenciando una dirección de memoria, que es lo que aproximadamente sucede al hacer &v (creo que me estoy haciendo mucho lío en vano :silbar:).
Me temo que si.

El punto importante a manejar es: cuando en una expresión se refiere a un array por su nombre (o utilizando una expresión) esto resulta en la dirección en memoria del primer elemento del array, denotar un array de tipo "T [N]" resulta en un puntero de tipo "T *".

El caso usual son las cadenas de caracteres "a la C":
Código (cpp) [Seleccionar]
char a[] = "Hola, mundo";
char *p = a;

cout << a << endl;
cout << p << endl;

No hay diferencia entre las dos llamadas porque en la primera el uso de "a" genera la dirección en memoria necesaria (de a[0]).

Los dolores de cabeza se pueden generar en los casos donde esa sustitución no se da, por ejemplo cuando el operando de '&' es un array. Ese operador resulta en la dirección en memoria del objeto y si este es de tipo "T" el puntero resultante es de tipo "T *". Aquí:
Código (cpp) [Seleccionar]
char a[] = "Hola, mundo";

cout <<  a << endl; // 1)  a ==> "char *"
cout << &a << endl; // 2) &a ==> "char (*)[12]"


En el caso 1:
1) El nombre del array resulta en la dirección de su primer elemento.
2) Ya que ese puntero es de tipo "char *" el operador "<<" imprime la cadena de caracteres ahí almacenada.

En el caso 2:
1.1) Primero se procesa "&a", el nombre del array no resulta en un puntero.
1.2) La dirección resultante del operador "&" es de tipo "char (*)[12]".
2) El operando de "<<" es una dirección de memoria pero no es de tipo "char *" y por ende no se procesa como en el caso 1.

Otro caso común es el operador "sizeof".

Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

zonahurbana

Cita de: rir3760 en 25 Julio 2013, 17:40 PM
Los dolores de cabeza se pueden generar en los casos donde esa sustitución no se da, por ejemplo cuando el operando de '&' es un array. Ese operador resulta en la dirección en memoria del objeto y si este es de tipo "T" el puntero resultante es de tipo "T *".
Muchas gracias por responder.
Pero la verdad es que no entiendo mucho a qué se refiere con "dirección en memoria del objeto". Es decir, he revisado los temas de clases y objetos, pero cuando se refiere a un array como objeto, ¿qué debería imaginarme exactamente? ¿Una instancia de una clase que contiene un array?

Agradecería mucho que nos explique un poco más acerca de:
Código (cpp) [Seleccionar]
char (*)[12]
O tal vez pueda decirme con qué nombre más o menos buscar este tema.

En este código:
Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;

int main()
{
    char v[]="Probando";
    cout<<v<<endl;
    cout<<&v[0]<<endl;
    cout<<(char*)v<<endl;
    cout<<&v<<endl;
    return 0;
}

Los primeros tres argumentos que se le envían a cout son exactamente lo mismo, ¿verdad?
Es decir, se le envía la dirección en memoria de v[0] y muestra la cadena.
Lo último muestra una dirección de memoria, ¿es la de v[0]?
Antes preguntaba si era la única forma de mostrar por pantalla dicha dirección de memoria.
Nunca dejar de aprender es importante, más allá del ritmo que se siga ...

rir3760

Cita de: zonahurbana en 25 Julio 2013, 23:43 PMno entiendo mucho a qué se refiere con "dirección en memoria del objeto".
Es la definición mas cruda: un objeto es un pedazo de memoria que se interpreta de acuerdo a su tipo y si tiene nombre se le conoce como variable. La POO es tema aparte.

Cita de: zonahurbana en 25 Julio 2013, 23:43 PMAgradecería mucho que nos explique un poco más acerca de:
Código (cpp) [Seleccionar]
char (*)[12]
O tal vez pueda decirme con qué nombre más o menos buscar este tema.
Es un puntero a array, puedes buscar información sobre arrays, punteros y la relación entre ellos.

Cita de: zonahurbana en 25 Julio 2013, 23:43 PMEn este código:
Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;

int main()
{
    char v[]="Probando";
    cout<<v<<endl;
    cout<<&v[0]<<endl;
    cout<<(char*)v<<endl;
    cout<<&v<<endl;
    return 0;
}

Los primeros tres argumentos que se le envían a cout son exactamente lo mismo, ¿verdad?
Correcto pero, y lo digo en buen plan, eso ya lo había explicado eferion:
Código (cpp) [Seleccionar]
&v[0] ==> &(*(v + 0)) ==> &(*(v)) ==> &*v ==> v

Cita de: zonahurbana en 25 Julio 2013, 23:43 PMLo último muestra una dirección de memoria, ¿es la de v[0]?
No. Esa es la dirección del array "v" y es de tipo "char (*)[9]" ya que al aplicar el operador "dirección de" sobre un array obtienes un puntero a ... array.

Y si realizas aritmética de punteros este (el puntero) se incrementa un numero de bytes igual al tamaño del array. Por ejemplo:
Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;
 
int main()
{
   char v[] = "Probando";
   
   cout << "Direccion base: " << (void *) v << endl;
   cout << " v + 2 == " << (void *) ( v + 2) << endl;
   cout << "&v + 2 == " << (void *) (&v + 2) << endl;
   
   return 0;
}


Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language