[C con Clase] Arrays bidimencionales y arrays de punteros

Davidson, Steven srd4121 en njit.edu
Jue Nov 1 04:55:01 CET 2012


Hola César,

2012/10/31 cesar arias <sinatra435 en hotmail.com>:
> Hola a todos, tengo una duda muy grande, espero me puedan ayudar.
>

Te voy comentando a medida que vaya leyendo tus afirmaciones.

> Suponiendo que tenemos la siguiente declaración:
>
> int A[4][4];
> int *B[4];
>
> Luego reservamos memoria para el array de punteros B con la función
> malloc().
>
> for(short x=0; x<4; x++)
>    B[t]=(int *)malloc(4 * sizeof(int));
>

Sospecho que querías escribir:

B[x] = (int *) malloc( 4 * sizeof(int) );

> Desde aquí tengo entendido que para pasar un puntero al array bidimencional
> como argumento a una función, así:
>
> pares(A);
>

Bueno, quiero aclarar que un array ES una dirección de memoria. Esto
implica que estamos copiando direcciones de memoria al pasar, 'A'. Por
lo tanto, no tenemos un puntero al array; el array puede ser tratado
como un puntero constante.

> el parámetro especificado debe ser A[][4], quedando el prototipo de la
> función como:
>
> void pares( int A[][4]);
>
> La constante 4 se especifica debido a que el compilador necesita saber
> cuanto espacio de memoria está reservado por cada fila y así saber donde
> empieza la siguiente fila.
>
> Pero para pasar un puntero a array de punteros como argumento de una función
> el prototipo debe ser:
>
> void pares( int *B[]);
>

Nuevamente, se trata de un array que es una dirección de memoria, por
lo que no hay "otro puntero", aunque sí puede convertirse en un tipo
de puntero, ya que ambos conceptos - arrays y punteros - involucran
direcciones de memoria.

> en donde no se especifica el espacio de memoria de cada fila apuntada por
> cada puntero.
>
> Mi pregunta es:
> Si para acceder a B dentro de la función se puede utilizar indexacion como
> en cualquier array bidimencional(aunque se que un array bidimencional y un
> array de punteros no es exactamente lo mismo) (ejem: B[1][0]), ¿Como sabe el

Sí. Se puede usar el operador [] con una dirección de memoria, tanto
si se trata de un puntero como de un array.

> compilador donde empieza cada "fila" apuntada por cada array?
>

Bueno, esto de "fila" puede variar según a qué nivel de conceptos
estamos hablando. Me imagino que con "fila" te refieres a cada array
dinámicamente adjudicado con 'malloc()', entonces la respuesta es que
son arrays "sueltos" en memoria. Recuerda que estamos ante un array de
punteros. El uso de cada puntero es independiente del uso de este
array, 'B'.

De hecho, lo que ocurre es que evaluamos cada operador [] en su orden
correspondiente. Por ejemplo, B[1][0] se evalúa así: (B[1])[0], o si
lo prefieres, así:

1. resultado <- B[1]
2. resultado2 <- resultado[0]

Es decir, accedemos al segundo elemento del array, 'B', que es un
puntero. Con este puntero, usamos el segundo operador [] para acceder
al primer elemento del array dinámico que es de tipo 'int'.

Quizá sea mejor que veas un vector descriptor antes del bucle 'for';
es decir, que acabamos de definir el array, 'B':

Dirección de  |
memoria        |   Tipo   |  Nombre  |       Valor       |  Misceláneo
-----------------+---------+------------+-----------------+--------------------------------
0xAAFF5500 |  int *     |  ----         | 0x005522AA | [array: B = 0xAAFF5500]
0xAAFF5504 |  int *     |  ----         | 0x8811EEFF | B[1]
0xAAFF5508 |  int *     |  ----         | 0x00008800  | B[2]
0xAAFF550C |  int *     |  ----        | 0x1111FFCC | B[3]
...

Y ahora, después del bucle 'for' y por tanto, después de la
adjudicación dinámica de la memoria:

Dirección de  |
memoria        |   Tipo   |  Nombre  |       Valor       |  Misceláneo
-----------------+---------+------------+-----------------+--------------------------------
0xAAFF5500 |  int *     |  ----         | 0xBBCC1100 | [array: B = 0xAAFF5500]
0xAAFF5504 |  int *     |  ----         | 0xCC00DD00 | B[1]
0xAAFF5508 |  int *     |  ----         | 0xDD99EE00 | B[2]
0xAAFF550C |  int *     |  ----         | 0xCC22BB00 | B[3]
...
0xBBCC1100 |  int       |  ----         | 1234              | B[0][0]
0xBBCC1104 |  int       |  ----         | -6721             | B[0][1]
0xBBCC1108 |  int       |  ----         | 0                    | B[0][2]
0xBBCC110C |  int       |  ----         | -999921        | B[0][3]
...
0xCC00DD00 |  int       |  ----         | -111332        | B[1][0]
0xCC00DD04 |  int       |  ----         | 88982           | B[1][1]
0xCC00DD08 |  int       |  ----         | 5                   | B[1][2]
0xCC00DD0C |  int      |  ----         | 77182            | B[1][3]
...
0xCC22BB00 |  int       |  ----         | 71872319      | B[3][0]
0xCC22BB04 |  int       |  ----         | -19023315     | B[3][1]
0xCC22BB08 |  int       |  ----         | 61441400      | B[3][2]
0xCC22BB0C |  int       |  ----         | -66489230    | B[3][3]
...
0xDD99EE00 |  int       |  ----         | 9866630321  | B[2][0]
0xDD99EE04 |  int       |  ----         | -23000558     | B[2][1]
0xDD99EE08 |  int       |  ----         | 161480009    | B[2][2]
0xDD99EE0C |  int       |  ----         | -30054899    | B[2][3]
...

Todo esto es suponiendo que 'int' ocupa 4 bytes y las direcciones de
memoria son de 32 bits (4 bytes).

Al aplicar los operadores [], simplemente vamos "saltando" de
dirección de memoria a dirección de memoria, hasta llegar a un valor
de tipo 'int'. Siguiendo el ejemplo de 'B[1][0]', tenemos estos pasos:

B[1]:
  <0xAAFF5500> + 1 (int *)  =>  <0xAAFF5500> + 4 (bytes)  =>  <0xAAFF5504>

El valor en esta dirección de memoria es: 0xCC00DD00

B[1][0]:
  <0xCC00DD00> + 0 (int)  =>  <0xCC00DD00> + 0 (bytes)  =>  <0xCC00DD00>

Cuyo valor es: -111332 de tipo 'int'.

El compilador no sabe cuántos elementos hay en una fila, porque no es
algo que haya creado. Creamos las "filas" dinámicamente, y por tanto,
el compilador no entra en juego.

> Ademas tengo otra duda:
> Como ven aprendo a programar en libros y no tengo quien me guié, me gustaría
> saber si es necesario saber de es estos temas así en profundidad porque en
> la universidad en la que estaba no vimos este tema en profundidad, aunque
> solo alcance a llegar asta segundo semestre. Por supuesto me estoy
> refiriendo a la carrera de sistemas.
>

Soy partidario de que sí deberías conocer estos temas, si piensas usar
C/C++. Ahora bien, en la práctica, es posible que no hagas mucho uso
de esto, porque el desarrollo actual de software se acerca a un nivel
alto, para que no tengamos que pensar en tales detalles. Sin embargo,
casi siempre aparece un caso parecido durante el desarrollo en el que
debes manejar arrays directamente. Personalmente, si no entiendes
punteros y arrays, no eres un buen programador de C/C++. Esto no
significa que nunca vayas a tener problemas de vez en cuando; incluso
los veteranos tenemos que pensar unos minutos en casos parecidos,
especialmente si tenemos algo así:

int ********************ptr;

Como esto es un cacao mental, uno normalmente intenta organizarse
mejor, lo que suele conducir a una implementación de mayor nivel para
que entendamos lo que ocurre y así seremos menos propensos a cometer
errores.


Espero haber aclarado las dudas.

Steven




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