[C con Clase] Punteros...

Steven Davidson steven en conclase.net
Mie Feb 28 21:04:54 CET 2007


Hola Alejandro,

El pasado 2007-02-28 17:30:38, Alejandro escribió:

A> Bueno, creo que ya voy empezando a entender algo. De todas maneras, hay cosas que me gustaría que me explicarais sobre este tema. Supuestamente, si yo tengo "int *p" y hago "cout << p" me muestra la dirección de memoria y "cout << *p" me muestra el contenido de esa dirección de memoria. Suponiendo que esto sea correcto:

Sí, aunque quiero expresar lo que has dicho de otro modo. Lo hago porque en estos momentos es crucial formar una buena base con la terminología. Por esta razón, voy a ser algo puntilloso, hasta que tengas soltura con este tema.

cout << p;

Esto te muestra (por pantalla) el valor de 'p' que es una dirección de memoria.

Efectivamente, con '*p', se mostrará el contenido de la variable apuntada por 'p'.

A> ¿En estas sentencias transformáis una variable char en un puntero a int y en un puntero genérico respectivamente?

Casi. No es una variable 'char', sino un array de 'char'. La palabra "transformar" puede dar lugar a equivocaciones. Digamos que el tipo del valor es temporalmente cambiado a otro tipo.

A> char cadena[10] = "Hola";
A> char *c;
A> int *n;
A> void *v;
A> c = cadena; // c apunta a cadena
A> n = (int *)cadena; // n también apunta a cadena
A> v = (void *)cadena; // v también
A> ¿Y en esta otra transformáis un puntero genérico en un puntero a float?
A> cout << "float: " << *(float *)v << endl;

Sí, pero date cuenta que posteriormente usamos el operador '*', por lo que después de realizar el cásting (cambio de tipo), accedemos a tal dirección de memoria para obtener el valor contenido en ella.

A> En el curso ponéis el siguiente ejemplo:
A> #include <iostream>
A> using namespace std;
A> struct stEstructura {
A> int a, b;
A> } estructura, *e;
A> int main() {
A> estructura.a = 10;
A> estructura.b = 32;
A> e = &estructura;
A> cout << "variable" << endl;
A> cout << e->a << endl;
A> cout << e->b << endl;
A> cout << "puntero" << endl;
A> cout << estructura.a << endl;
A> cout << estructura.b << endl;
A> cin.get();
A> return 0;
A> }
A> En el "cout << e->a;" mostráis el contenido de la variable "estructura.a" si no me equivoco. Sin embargo no utilizáis el operador "*" cuando con variables 

Exacto. Esto es porque el operador -> nos libra de escribir la "indirección y acceso a miembro". Ten presente que el operador . tiene mayor precedencia que el operador *. Por ejemplo, lo siguiente sería un error:

cout << *e.a << endl;

Accedemos al miembro 'a', y luego intentamos leer el valor apuntado. Sin embargo, 'a' no es un puntero, y por consiguiente no se puede aplicar el operador de indirección.

La solución, es usar paréntesis, para cerciorarnos del orden de las operaciones. Esto sería,

cout << (*e).a << endl;

Con esto, accedemos a la estructura apuntada, y luego accedemos al miembro 'a' de tal estructura.

Como esto suele ser un engorro, y puede provocar problemas de legibilidad, los diseñadores de C/C++, optaron por crear otro operador que hace exactamente esto: "indirección (de un puntero) y acceso a miembro de una estructura". Además, a la hora de traducir esta operación, seguramente genera una sola instrucción del procesador para ella. El ejemplo anterior sería sencillamente,

cout << e->a << endl;

> había que usarlo. Dicho esto, ¿cómo podemos mostrar la dirección de memoria de "estructura.a"?

Como he explicado anteriormente, el operador . es evaluada antes que * y también de &. Por lo tanto, escribe,

cout << &e->a << endl;

Lo más seguro es que la dirección de memoria sea igual que la que está guardada en 'e'. O sea,

cout << e << " = " << &e->a << endl;

A> Por último:
A> #include <iostream>
A> using namespace std;
A> int main() {
A> char cadena1[] = "Cadena 1";
A> char *cadena2 = "Cadena 2";
A> cout << cadena1 << endl;
A> cout << cadena2 << endl;
A> //cadena1++; // Ilegal, cadena1 es constante
A> cadena2++; // Legal, cadena2 es un puntero
A> cout << cadena1 << endl;
A> cout << cadena2 << endl;
A> cout << cadena1[1] << endl;
A> cout << cadena2[0] << endl;
A> cout << cadena1 + 2 << endl;
A> cout << cadena2 + 1 << endl;
A> cout << *(cadena1 + 2) << endl;
A> cout << *(cadena2 + 1) << endl;
A> cin.get();
A> return 0;
A> }
A> Me podéis explicar qué hacéis en las siguientes sentencias.
A> cout << cadena1 + 2 << endl;
A> cout << cadena2 + 1 << endl;
A> cout << *(cadena1 + 2) << endl;
A> cout << *(cadena2 + 1) << endl;

Esto se explica anteriormente en el capítulo; específicamente: http://c.conclase.net/curso/index.php?cap=012b

Básicamente, incrementamos por 1 ó 2 elementos al dirección en 'cadena1' y en 'cadena2'. Recuerda que un array puede ser pensado como un puntero constante. Como el array y el puntero son de tipo 'char', incrementamos por 1 ó 2 bytes - 'char' siempre ocupa 1 byte.

Dicho esto, vemos que, las dos últimas sentencias producen el mismo comportamiento que si usáramos el operador de acceso a elemento []. Esto es,

cout << cadena1[2] << endl;
cout << cadena2[1] << endl;

A> Bueno, un saludo y a ver si soy capaz de entender este capítulo. Más vale que sea importante, porque es dificilísimo.

Jeje... Sí, es importante y sí, es difícil entender a la primera, pero no te desanimes mucho al principio. Todos hemos pasado por lo mismo.


Espero haber aclarado las dudas.

Steven


Más información sobre la lista de distribución Cconclase