[C con Clase] Capitulo 12c

Steven Davidson steven en conclase.net
Dom Feb 4 21:17:25 CET 2007


Hola David,

El pasado 2007-02-04 11:00:39, David escribió:

D> Hola a todos. Verán, estaba haciendo este ejemplo y me fije en los bits que debería ocupar Mes y Mes2, pero no entiendo bien Mes2[], aquí les pongo el ejemplo:
D> #include <iostream>
D> using namespace std;
D>  
D> int main() { 
D>    char Mes[][11] = { "Enero", "Febrero", "Marzo", "Abril", 
D>       "Mayo", "Junio", "Julio", "Agosto", 
D>       "Septiembre", "Octubre", "Noviembre", "Diciembre"}; 
D>    char *Mes2[] = { "Enero", "Febrero", "Marzo", "Abril", 
D>       "Mayo", "Junio", "Julio", "Agosto", 
D>       "Septiembre", "Octubre", "Noviembre", "Diciembre"};
D>  
D>    cout << "Tamaño de Mes: " << sizeof(Mes) << endl; 
D>    cout << "Tamaño de Mes2: " << sizeof(Mes2) << endl; 
D>    cout << "Tamaño de cadenas de Mes2: " 
D>         << &Mes2[11][10]-Mes2[0] << endl; 
D>    cout << "Tamaño de Mes2 + cadenas : " 
D>         << sizeof(Mes2)+&Mes2[11][10]-Mes2[0] << endl;
D>  
D>    cin.get();
D>    return 0; 
D> }sizeof(Mes) me devuelve 132, osea 12*11, en cambio, sizeof(Mes2) ne devuelve 48 o sea 4*12, alguién me puede explicar porque sizeof(Mes2) no cuenta los elementos restantes del vector?

Ten en cuenta los tipos de cada variable. Tenemos que 'Mes' es un array de 132 caracteres, mientras que 'Mes2' es un array de 12 punteros a 'char'. Suponiendo que cada puntero ocupa 4 bytes, entonces tenemos 4*12 = 48 bytes.

Un array no es igual a un puntero. Podemos usar un puntero para apuntar a la información, pero un array ES la información.

D> Además no entiendo esta operación: &Mes2[11][10]-Mes2[0], se supone que esta operación cuenta la distancia que hay entre un puntero y otro, pero la distancia que a mi me da entre el último elemento y el primer elemento es de 106.

Lo único que podemos asegurar es que los 12 punteros existen en memoria contiguamente. Sin embargo, las cadenas apuntadas por cada puntero no tienen por qué estar contiguas entre sí.

D> Como dice el texto: "cada puntero de Mes2 es una cadena de la longitud adecuada 
D>   para almacenar cada mes." Capitulo 12c Variables IV - CconClase.

Creo que es mejor reescribir lo anterior para que diga esto:

"cada puntero de Mes2 *apunta* a una cadena de la longitud adecuada para almacenar cada mes."

D> Si esto es asi, la distancia entre el último puntero y el primero debería ser, si no me equivoco 87, porque estoy contando desde el segundo byte de Enero hasta el penúltimo byte de Diciembre para btener la distancia que los separa, al fin y al cabo terminan siendo char, un byte por caracter.

Creo que he explicado el tema con lo ue ya vengo diciendo, pero lo reitero por si acaso. Sólo podemos calcular la "distancia" entre punteros, pero no con la las cadenas apuntadas, ya que éstas pueden o no estar contiguas en memoria. Esto ya depende del compilador, si quiere y puede guardar las cadenas contiguamente.

D> Además, si el puntero Mes2 se adapta al número de elementos que hay, como es posible que exista la posición [11][10] dentro de Mes2, si "Diciembre" tiene 9 elementos + 1 que sería \0, osea, que realmente debería ser Mes2[11][9] que sería el últio caracter "\0" de la cadena diciembre.

Las posiciones o direcciones de memoria simepre existen. Lo que tenemos que andar con cuidado es a la hora de usar las direcciones de memoria. No todas las direcciones son válidas para nuestro uso. Dicho esto, puedo hacer lo siguiente:

char lista[10];

cout << lista[12345];  // Peligro

Lo anterior lo puedo hacer, pero claro está, estoy en otra parte de la memoria. El sistema operativo es quien dicta si esta operación es válida o no. Es posible que el S.O. nos dé un error diciendo algo como "violación de acceso: lectura". En otras palabras, no nos deja acceder a esa posición para leer el byte en ella.

Como ya he dicho, las posiciones o direcciones de memoria simepre existen. Lo que puede o no existir es la validez a esas direcciones. C/C++ no verifica la validez de los índices de los arrays. C++ mantiene la regla aplicada bajo C: "el programador sabe lo que hace". Con esta "simpleza", la responsabilidad es nuestra de programar correctamente; C/C++ no se hace cargo de cosas que no deberías hacer. Obviamente, C/C++ sí se encarga de la sintaxis y de la semántica del lenguaje, pero escribir 'lista[1234]' es gramaticalmente correcto.

D> Evidentemente, SÉ que ESTOY EQUIVOCADO porque los resultados que me está dando el compilador son completamente distintos a los que estoy mostrando yo aquí.
D> Sinceramente no le pillo el truco a leer el tamaño del puntero en bytes, si alguien me lo puede explicar se lo agradecería mucho. 

Lo que estás intentando hacer es aplicar la lógica de un array a un puntero. Esto casi nunca sale bien. O sea, intentas hacer esto:

char array[100];
char *p = array;

cout << sizeof array << " = " << sizeof p << " ?\n";

Aparecerá en pantalla algo así:

100 = 4 ?

Obviamente, no son iguales. Sin embargo, las cantidades son correctas. El puntero 'p' ocupa 4 bytes ya que apunta a la cadena, mientras que 'array' ES la dirección del primer elemento de la cadena. Además, el compilador sabe cuánta información contiene 'array'. Dicho esto, extendamos el ejemplo,

cout << &array << " = " << &array[0] << " ?\n";
cout << &p << " = " << &p[0] << " ?\n";

Aparecerá en pantalla algo como:

ff00ee11 = ff00ee11
44aacc00 = ff00ee11

Como puedes ver, 'p' no tiene nada que ver con los elementos de 'array', pero 'array' sí está relacionado con la cadena contenida.


Quizá sea mejor ver un vector descriptor de la memoria usando el ejemplo del curso. Como es mucha información, sólo la presentaré parcialmente. Esto sería,

Dirección
de Memoria    Tipo     Nombre     Valor        Misceláneo
-----------------------------------------------------------------
0x11EEAA00    char     ----       'E'          (char Mes[12][11])
0x11EEAA01    char     ----       'n'
0x11EEAA02    char     ----       'e'
0x11EEAA03    char     ----       'r'
0x11EEAA04    char     ----       'o'
0x11EEAA05    char     ----       '\0'
0x11EEAA06    char     ----       '='
0x11EEAA07    char     ----       '?'
0x11EEAA08    char     ----       '%'
0x11EEAA09    char     ----       '`'
0x11EEAA0A    char     ----       '}'
0x11EEAA0B    char     ----       'F'
0x11EEAA0C    char     ----       'e'
0x11EEAA0D    char     ----       'b'
0x11EEAA0E    char     ----       'r'
0x11EEAA0F    char     ----       'e'
0x11EEAA10    char     ----       'r'
0x11EEAA11    char     ----       'o'
0x11EEAA12    char     ----       '\0'
0x11EEAA13    char     ----       '#'
0x11EEAA14    char     ----       '\0'
0x11EEAA15    char     ----       '|'
0x11EEAA16    char     ----       '}'
0x11EEAA17    char     ----       'M'
0x11EEAA18    char     ----       'a'
0x11EEAA19    char     ----       'r'
0x11EEAA1A    char     ----       'z'
0x11EEAA1B    char     ----       'o'
0x11EEAA1C    char     ----       '\0'
0x11EEAA1D    char     ----       '\n'
0x11EEAA1E    char     ----       '\0'
0x11EEAA1F    char     ----       '\0'
0x11EEAA20    char     ----       '\a'
...
0x11EEAA84    char *   ----       0x55CCFF00     (char *Mes2[12])
0x11EEAA88    char *   ----       0x33BBFF00
0x11EEAA8C    char *   ----       0xAA99DD00
0x11EEAA90    char *   ----       0xAA88FF00
0x11EEAA94    char *   ----       0xFF44CC00
0x11EEAA98    char *   ----       0x22EE8800
0x11EEAA9C    char *   ----       0x22119900
0x11EEAAA0    char *   ----       0x55AA4400
0x11EEAAA4    char *   ----       0x3355DD00
0x11EEAAA8    char *   ----       0xFF113300
0x11EEAAB0    char *   ----       0xFF882200
0x11EEAAB4    char *   ----       0xFF99CC00
...
0x22119900    char     ----       'J'
0x22119901    char     ----       'u'
0x22119902    char     ----       'l'
0x22119903    char     ----       'i'
0x22119904    char     ----       'o'
0x22119905    char     ----       '\0'
...
0x22EE8800    char     ----       'J'
0x22EE8801    char     ----       'u'
0x22EE8802    char     ----       'n'
0x22EE8803    char     ----       'i'
0x22EE8804    char     ----       'o'
0x22EE8805    char     ----       '\0'
...
0x33BBFF00    char     ----       'F'
0x33BBFF01    char     ----       'e'
0x33BBFF02    char     ----       'b'
0x33BBFF03    char     ----       'r'
0x33BBFF04    char     ----       'e'
0x33BBFF05    char     ----       'r'
0x33BBFF06    char     ----       '0'
0x33BBFF07    char     ----       '\0'
...

Y así sucesivamente. Como puedes ver, los punteros del array 'Mes2' están juntos en un solo bloque. Esto es exactamente lo que hace un array: todos los elementos existen contiguamente en un solo bloque. Sin embargo, los punteros apuntan a direcciones de memoria donde se encuentre la información. Esta información puede encontrarse arbitrariamente en cualquier bloque de memoria. En el ejemplo anterior, el compilador decidirá qué hacer y dónde poner la información: los arrays y las cadenas de caracteres.


Espero que todo esto aclare las dudas.

Steven


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