[C con Clase] Punteros arrays y memoria.

Steven Davidson steven en conclase.net
Jue Ene 11 22:42:24 CET 2007


Hola Daniel,

El pasado 2007-01-11 17:36:01, dani escribió:
 
d> Buenas a todos,
d> antes de nada os comento que yo programaba en Fortran y me decidí a pasar a
d> C porque me dijeron que era mejor y a mi me interesa ya que trabajo haciendo
d> simulaciones numéricas. Lo que más me interesa ahora es el ahorro de memoria
d> cuando trabajo con matrices muy grandes. Mirando el manual de C con clase
d> he visto como definir un puntero (ej: **A) que me permite crear una matriz
d> a mitad de programa con la dimensión que corresponda (y así no tener que
d> hacer dos programas, uno para calcular la dimensión y otro para trabajar con
d> las matrices), sin embargo resulta que los punteros ocupan memoria (N*M
d> espacios de memoria adicionales para los punteros) que no es necesaria
d> definiendo la matriz al inicio del programa (ej: A[N][M]) como un array de

Sí y no. Si quieres implementar una matriz de N x M, entonces sólo necesitas N punteros, no NxM. Esto sería,

cantidad_total = N * M * [int] + N * [int *] + [int **]

En general, esto vendría a equivaler:
cantidad_total = N * M * 4 + N * 4 + 4
               = 4 * (N * M + N + 1)

d> dos dimensiones. Así que mi pregunta es si un  programa sencillo como
d> #include <iostream>
d> void funcion(int B[1][2]);
d> int main() {
d>     int A[1][2];
d>     A[0][0]=1; A[0][1]=2; A[0][2]=3;
d>     A[1][0]=4; A[1][1]=5; A[1][2]=6;
d>     funcion(A);
d>     printf("A =  %i  %i  %i \n",A[0][0], A[0][1], A[0][2]);
d>     printf("     %i  %i  %i \n",A[1][0], A[1][1], A[1][2]);
d>     getchar();
d> }
d> void funcion(int B[1][2]){
d>      B[0][0]=9; B[0][1]=9; B[0][2]=9;
d>      B[1][0]=9; B[1][1]=9; B[1][2]=9;
d> }

Me temo que tienes unos errores en el código anterior. Las declaraciones de los arrays bidimensionales son incorrectas; deberían ser:

void funcion( int B[2][3] );

int main()
{
  int A[2][3];   // 2 x 3
  ...
}

d> es más económico que
d> #include <iostream>
d> void funcion(int **B);
d> int main() {
d>    int i,n,m,**A;
d>    n=2; m=3;
d>    A=new int *[n];

Aquí creas 'n' punteros.

d>    for (i=0;i<n;i++){A[i]=new int [m];}

Aquí, creamos 'm' columnas por cada puntero (o fila).

d>    A[0][0]=1; A[0][1]=2; A[0][2]=3;
d>    A[1][0]=4; A[1][1]=5; A[1][2]=6;
d>    funcion(A);
d>    printf("A =  %i  %i  %i \n",A[0][0], A[0][1], A[0][2]);
d>    printf("     %i  %i  %i \n",A[1][0], A[1][1], A[1][2]);

Si usas C++, sugiero usar 'cout <<'.

d>    for (i=0;i<n;i++){delete[] A[i];}
d>    delete[] A;
d>    getchar();
d> }
d> void funcion(int **B){
d>    B[0][0]=9; B[0][1]=9; B[0][2]=9;
d>    B[1][0]=9; B[1][1]=9; B[1][2]=9;
d> }

Vamos a debatir tu comentario "es más económico". En cuanto a memoria, quizá sea más económico, pero si nos basamos en lo que dijiste al comienzo: "trabajo con matrices muy grandes", entonces usar arrays estáticos no es conveniente ni a veces plausible. Esto es porque si estamos ante una matriz grande, por ejemplo del orden de 10.000 x 10.000, entonces estamos tratando unos 381,470 MB. Esto es muchísima memoria porque toda la memoria existe en un solo bloque. Es decir, el sistema operativo tiene que encontrar un bloque de memoria contigua de 381 MB. Esto quizá sea posible o no, ya que típicamente la memoria está fragmentada a lo largo de la "sesión". Encontrar memoria libre para un solo bloque de 381 MB es bastante difícil. También tienes que tener en cuenta que las variables locales usan una memoria restrictiva del programa a ejecutar. Si usas memoria dinámicamente, entonces se usa otro tipo de memoria preparada por el S.O.. 

Por estas razones, sugiero usar memoria dinámicamente de la manera en que lo implementaste anteriormente. Esto es porque creamos varios bloque de memoria contigua pero de cantidades más pequeñas. Tomando el ejemplo anterior, tendríamos 10.000 bloques de 39 KB. Esto es más plausibled, ya que puede haber varios fragmentos de memoria libre para poder reservar bloques de 39 KB.

d> Si ahora tengo una matriz simétrica (A[i][j]=A[j][i]) y quiero ahorrar la
d> memoria repetida lo que tendré es una "matriz triangular".  Mi pregunta
d> es si me creo una estructura para almacenar esto la estructura también
d> almacena punteros o no, ahorro  memoria con la estructura o es la misma que
d> creando un puntero con la forma de la estructura.

Si he entendido bien, creo que da igual una manera u otra. Sugiero crear un doble puntero. En lugar de crear el mismo array dentro del bucle, tendrás que formular una manera para ir restando las cantidades. Por ejemplo,

for( i=0; i<n; i++ )  A[i] = new int[m-i];

Tenemos un ejemplo parecido a lo que comentas en el capítulo 17 del curso de C++. Puedes ir directamente a: http://c.conclase.net/curso/index.php?cap=017

d> Una ultima preguntita más. Como puedo medir el tamaño de una estructura y
d> sus punter para ver si me paso de la memoria RAM de mi ordenador.

Usa el operador 'sizeof' para averiguar la cantidad de bytes necesaria para la expresión o tipo de dato. Por ejemplo,

struct stInfo
{
  int **ptr;
  int n,m;
};

cout << "Cantidad = " << sizeof( stInfo ) << " bytes\n";


Espero haber aclarado las dudas.

Steven


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