[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