[C con Clase] matrices y punteros

Steven Davidson steven en conclase.net
Dom Jun 10 06:09:00 CEST 2007


Hola Rodolfo,

El pasado 2007-06-10 03:08:37, rodolfo escribió:

r> /*
r>     Hola.-
r>     Agradezco a Steven y a Salvador por sus explicaciones  acerca  de porque
r>     strcpy(), y no una copia simple de punteros, como siempre muy 
r> didacticos,
r>     asi que todo aclarado.

Excelente. Ya de paso quiero agregar a lo que dije. Copiar directamente los datos miembros entre objetos, los cuales contienen punteros, se llama una "copia de poca profundidad", ya que copiamos la "superficie" sin adentrarnos a los datos siendo apuntados. Si seguimos los punteros de tales datos miembros para hacer la copia, entonces se habla de una "copia profunda".

En la mayoría de los casos queremos hacer copias profundas, para que cada objeto sea eso mismo: una entidad única. Como mencionó Salvador, podemos hacer copias de poca o baja profundidad, pero debemos tener en cuenta su significado y ser conscientes que tales objetos dependen de datos fuera de su alcance. Esto viene bien si tenemos una base de datos central la cual contiene toda la información, y nuestros objetos simplemente se limitan a referirse a los datos en tal base. Los datos son comunes y son tratados indirectamente a través de los objetos. Un ejemplo de esto es en aplicaciones de modelado 3D, donde los vértices existen únicamente, y no son copiados cada objeto. Esto lo hacemos por motivos de la velocidad de ejecución de susodichas aplicaciones.


Veamos el código y las preguntas.

r> #include <iostream>
r> using namespace std;
r> void ImprimeMatriz( double mat_ref[][4] )
r> {
r>    //  cout << setfixed(2);
r>      for(int i = 0; i<=2; i++)
r>      {
r>         for(int j = 0; j<=3; j++)
r>             cout << mat_ref[i][j] << "\t";
r>         cout << "\n";
r>      }
r>      cout << "\n";
r> }
r> int main()
r> {
r>     double mat[3][4] = {{0.0,0.1,0.2,0.3},
r>                         {1.0,1.1,1.2,1.3},
r>                         {2.0,2.1,2.2,2.3}};
r>     ImprimeMatriz(mat);
r>     int i = 1, j = 2;
r>     double* p = &mat[0][0];
r>     cout << "mat              " << mat  << "\tmuestra la direccion final a 
r> la que\n";
r>     cout <<                         "\t\t\t\tapunta mat, osea la de 
r> mat[0][0]\n";
r>     cout << "&mat             " << &mat  << "\t1. porque no muestra la 
r> direcc de mat?\n";

Me temo que estás confundiendo un array con un puntero. Son diferentes tipos aunque manejen el mismo tipo de dato: direcciones de memoria. El puntero es una variable que contiene una dirección de memoria como su valor. El array ES una dirección de memoria; no tiene tal "valor". Por ello, al usar 'mat', aparece la dirección de memoria, pero no la de la "variable" 'mat' sino la del primer elemento en tal array. Esto es, 'mat' == '&mat[0][0]'.

El compilador sustituye 'mat' por la dirección de memoria y aplica cualesquier cálculos necesarios si se usan índices. En realidad no usa la dirección de memoria, pero para la explicación, diremos que sí. Por esta razón, no existe '&mat'.

En el caso del puntero, existe memoria para la variable - el puntero en sí - para guardar una dirección de memoria como su valor.

r>     cout << "*mat             " << *mat  << "\tmuestra el contenido de la 
r> direccion\n";
r>     cout <<                         "\t\t\t\tcontenida en mat\n\n";
r>     cout << "\nmat+1            " << mat+1 << "\tmuestra la direccion final 
r> a la que\n";
r>     cout <<                         "\t\t\t\tapunta o sea la direccion de 
r> mat[1][0]\n";
r>     cout << "*(mat+1)         " << *(mat+1) << "\tmuestra el contenido de la 
r> direccion\n";
r>     cout <<                         "\t\t\t\tque contiene mat+1\n";
r>     cout << "mat[1]           " << mat[1] << "\tdireccion final a que apunta 
r> mat[1]\n";

Esto es correcto. Como ya he mencionado, arrays y punteros son diferentes tipos, pero como ambos manejan direcciones de memoria, existen correlaciones entre ambos. Por ejemplo, podemos convertir un array a un puntero constante, y viceversa, pero obviamente has cierto punto.

r>     cout << "&mat[1]          " << &mat[1] << "\t2. porque no muestra la 
r> direccion de mat[1]\n";
r>     cout << "*mat[1]          " << *mat[1] << "\t\tmuestra el contenido de 
r> la direccion\n";
r>     cout <<                              "\t\t\t\tque contiene mat[1]\n\n";

Para responder a este tipo de preguntas, siempre deberías comprobar el tipo de cada valor. Empecemos con:

mat     Su tipo es: double [][4]
mat[1]  Su tipo es: double [4]

Como puedes ver se trata de una dirección de memoria. Por lo tanto, al hacer '&mat[1]' realmente significa que quieres averiguar la dirección de memoria de la dirección de memoria. Esto no tiene sentido, y por ello no es posible.

r>     cout << "\n&mat[0][1]       " << &mat[0][1] << "\t3. porque aqui si 
r> muestra la direccion\n";
r>     cout <<                              "\t\t\t\tde mat[0][1]\n";

Esto es correcto. Comprobemos los tipos de cada valor:

mat        Su tipo es: double [][4]
mat[0]     Su tipo es: double [4]
mat[0][1]  Su tipo es: double

Al aplicar el operador & a una variable o lvalue (valor-izquierdo), obtenemos su dirección de memoria.


Espero haber aclarado las dudas.

Steven


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