Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


4.9.13  Operador sizeof

§1  Sinopsis

El operador sizeof informa del tamaño de almacenamiento utilizado por cualquier objeto, sea un tipo básico o derivado.

§2  Sintaxis

El operador unitario sizeof tiene dos formas posibles de sintaxis:

sizeof expresión-unitaria
sizeof (nombre-de-tipo)

Observe que a pesar del aspecto de la segunda forma de sintaxis, se trata de un operador, no de una función. sizeof representa una forma cómoda de obtener información sobre el tamaño de los tipos básicos.

§3  Resultado

Cualquiera que sea la forma de sintaxis empleada, el resultado es una constante entera de tipo size_t , con el espacio de memoria (bytes) usada por el operando. Con algunas excepciones, este tamaño es determinado por el tipo de operando, y para cada tipo, depende de la implementación. Es decir, no está predeterminado por el Estándar sino por el compilador utilizado (ver en 2.2.4c unas notas adicionales sobre los tipos básicos en C++).

§4  Ejemplo

#include <iostream.h>


int main(void) {             // ===============
  long double ld;
  class C { public: float x; float y; } c;
  cout << "Tamaño de ld: " << sizeof ld << endl;
  cout << "Tamaño de C: "  << sizeof c << endl;
  cout << "Long double = " << sizeof(long double) << " Bytes" << endl;
  return 0;
}

Salida:

Tamaño de ld: 10
Tamaño de C: 8
Long double = 10 Bytes

Comentario

Cuando se utiliza con instancias de tipos [3] se usa la primera forma de la sintaxis; tanto si se trata de tipos básicos (caso de ld en el ejemplo), como si son tipos definidos por el usuario (caso de C). Cuando se utiliza con especificadores de tipo básicos, se utiliza la segunda, con paréntesis (caso de long double).


§5  En el ejemplo que sigue se muestra claramente los criterios de uso de una y otra forma de sintaxis:

#include <iostream.h>

int main(void) {       // ===============
  int i;
  char ch;
  class Cl { long double ld; int i; } c;
  struct Es { long double ld; int i; } e;
  enum En { UNO } u;

  cout << "Tipo-int      == " << sizeof(int) << endl;
  cout << "Tipo-char     == " << sizeof(char) << endl;
  cout << "Tipo-Clase Cl == " << sizeof(Cl) << endl;
  cout << "Tipo-Estr. Es == " << sizeof(Es) << endl;
  cout << "Tipo-Enum En  == " << sizeof(En) << endl;

  cout << "Objeto-int   i == " << sizeof i << endl;
  cout << "Objeto-char ch == " << sizeof c << endl;
  cout << "Objeto-Clase c == " << sizeof c << endl;
  cout << "Objeto-Estr  e == " << sizeof e << endl;
  cout << "Objeto-Enum  u == " << sizeof u << endl;
  return 0;
}

Salida:

Tipo-int      == 4
Tipo-char     == 1
Tipo-Clase Cl == 16
Tipo-Estr. Es == 16
Tipo-Enum En  == 4
Objeto-int   i == 4
Objeto-char ch == 16
Objeto-Clase c == 16
Objeto-Estr  e == 16
Objeto-Enum  u == 4


§6  Compruebe la salida del ejemplo siguiente con la información suministrada en la documentación del compilador ( 2.2.4):

#include <iostream.h>

void main(void) {      // ===============
  cout << " Unsigned char == " << sizeof(unsigned char) << endl;
  cout << "   Signed char == " << sizeof(signed char) << endl;
  cout << "  Signed short == " << sizeof(short) << endl;
  cout << "Unsigned short == " << sizeof(unsigned short) << endl;
  cout << "  Unsigned int == " << sizeof(unsigned) << endl;
  cout << "    Signed int == " << sizeof(int) << endl;
  cout << " Unsigned long == " << sizeof(unsigned long) << endl;
  cout << "   Signed long == " << sizeof(long) << endl;
  cout << "         float == " << sizeof(float) << endl;
  cout << "        double == " << sizeof(double) << endl;
  cout << "   Long double == " << sizeof(long double) << endl;
}

Salidas (tamaños en bytes para distintos compiladores):

Linux GCC v 2.95.3 19991030

Borland C++ 5.5

MS Visual C++ 6.0

 Unsigned char == 1
   Signed char == 1
  Signed short == 2
Unsigned short == 2
  Unsigned int == 4
    Signed int == 4
 Unsigned long == 4
   Signed long == 4
         float == 4
        double == 8
  Long double == 12
 Unsigned char == 1
   Signed char == 1
  Signed short == 2
Unsigned short == 2
  Unsigned int == 4
    Signed int == 4
 Unsigned long == 4
   Signed long == 4
         float == 4
        double == 8
  Long double == 10
 Unsigned char == 1
   Signed char == 1
  Signed short == 2
Unsigned short == 2
  Unsigned int == 4
    Signed int == 4
 Unsigned long == 4
   Signed long == 4
         float == 4
        double == 8
   Long double == 8


§7  En el primer caso de la sintaxis , el tipo del operando (expresión-unitaria) es determinado sin evaluar la expresión, y por tanto sin efectos colaterales. Ejemplo:

#include <iostream.h>

int main(void) {             // ===============
  int i = 1;
  cout << "Valor i == " << i << endl;
  cout << "Tamaño de objeto-int i == " << sizeof ++i << endl;
  cout << "Valor i == " << i << endl;
  return 0;
}

Salida:

Valor i == 1
Tamaño de objeto-int i == 4
Valor i == 1


§8  El operador sizeof no puede ser sobrecargado ( 4.9.18).

§9  Puede utilizarse sizeof en directivas de preprocesador (esto es específico de C++Builder).

§10  sizeof con tipos char

  Cuando el operando es de tipo char (con o sin signo) el resultado es 1. Por definición, el resultado de sizeof(char) es siempre 1. Una definición bastante curiosa, ya que desde siempre estamos acostumbrados a que 1 Byte == 1 Octeto == 8 bits. Pero el estándar ANSI C++ establece que 1 Byte es el espacio de almacenamiento necesitado por un carácter, con independencia del número de bits que ocupe en la máquina; aunque hay que reconocer que (por el momento que sepamos) en todos los sistemas un carácter ASCII se almacena en un octeto ( 2.2.4). En todos los demás casos, el operador proporciona el número de bytes reales ocupados por el operando.

Dicho en otras palabras: podría existir una implementación en la que el 1 resultado de sizeof(char) no significara un "octeto", sino otro tamaño. Por ejemplo, 12 bytes.

Nota: la razón última de esta singularidad es que los tamaños de los tipos básicos C++ se definen como múltiplos del tamaño de un char (no de un octeto), con lo que no puede haber un tamaño de digamos 3.5 char. Así pues, el tamaño de un char es 1.

§11  sizeof con matrices

Cuando el operando es una matriz (que no sea un parámetro), puede utilizarse cualquiera de las dos sintaxis, y el resultado es el tamaño total de la matriz. En otras palabras: la etiqueta de la matriz no es considerada un puntero [1].

Considere el siguiente ejemplo:

#include <iostream.h>

int main(void) {                 // ===============
  char ach[5] = {'A','E','I','O','U'};
  cout << "Tamaño de la matriz ach: " << sizeof(ach) << endl;
  cout << "Tamaño de la matriz ach: " << sizeof ach << endl;

  int ain[5] = {1, 2, 3, 4, 5};
  cout << "Tamaño de la matriz ain: " << sizeof(ain) << endl;
  cout << "Tamaño de la matriz ain: " << sizeof ain << endl;

  long double ald[5] = { 1, 2, 3, 4, 5 };
  cout << "Tamaño de la matriz ald: " << sizeof(ald) << endl;
  cout << "Tamaño de la matriz ald: " << sizeof ald << endl;
}

Salida:

Tamaño de la matriz ach: 5
Tamaño de la matriz ach: 5
Tamaño de la matriz ain: 20
Tamaño de la matriz ain: 20
Tamaño de la matriz ald: 50
Tamaño de la matriz ald: 50

Comprobamos que en todos los casos se obtienen los tamaños esperados según la naturaleza de los elementos almacenados. Lo mismo ocurre si se trata de matrices de más de una dimensión:

#include <iostream.h>

int main(void) {         // ===============
  char ach[2][5];
  cout << "Tamaño de ach: " << sizeof ach << endl;
  cout << " ach[0]: " << sizeof ach[0] << endl;
  cout << " ach[0][0]: " << sizeof ach[0][0] << endl;

  int ain[2][5];
  cout << "Tamaño de ain: " << sizeof ain << endl;
  cout << " ain[0]: " << sizeof ain[0] << endl;
  cout << " ain[0][0]: " << sizeof ain[0][0] << endl;

  long double ald[2][5];
  cout << "Tamaño de ald: " << sizeof ald << endl;
  cout << " ald[0]: " << sizeof ald[0] << endl;
  cout << " ald[0][0]: " << sizeof ald[0][0] << endl;
 << "Tamaño de la matriz ald: " << sizeof ald << endl;
}

Salida:

Tamaño de ach: 10
       ach[0]: 5
    ach[0][0]: 1
Tamaño de ain: 40
       ain[0]: 20
    ain[0][0]: 4
Tamaño de ald: 100
       ald[0]: 50
    ald[0][0]: 10

Observe que en cualquier caso, los tamaños son también los esperados, y que la expresión mat[x] representa la fila x (0 <= x < a ) de una matriz multidimensional mat[a][b]...[n].


§11.1  El operador sizeof no puede utilizarse con matrices dinámicas o externas. Por ejemplo, el código

extern int m1[];          // L.1
extern char m2[5];        // L.2
int main () {
  ...
  cout << "Tamaño de m1: " << sizeof m1 << endl;   // L.5  ERROR!!
  cout << "Tamaño de m2: " << sizeof m2 << endl;   // L.6

produciría un error de compilación en L.5, ya que aunque la declaración de matriz en L.1 sea correcta, el compilador no dispone de suficiente información para obtener el valor solicitado. En cambio, L.6 compila sin dificultad porque gracias a la declaración L.2 dispone de la información pertinente.

§11.2  El número de elementos de una matriz se calcula mediante expresiones del tipo:

sizeof matriz1d / sizeof matriz1d[0];
sizeof matriz2d / sizeof matriz2d[0][0];
sizeof matriz3d / sizeof matriz3d[0][0][0];
...
sizeof matriznd / sizeof matriznd[0][0]...[0];

Observe que se trata siempre del cociente tamaño-total / tamaño-de-elemento. Considere el siguiente ejemplo:

#include <iostream.h>
#include <stdio.h>

int main(void) {       // ===============
  int ai[2][5][3];
  printf("Tamaño de ai:%4i bytes; tiene:%3i elementos\n",
             sizeof ai, sizeof ai / sizeof ai[0][0][0]);
  printf("       ai[0]:%4i bytes; tiene:%3i elementos\n",
          sizeof ai[0], sizeof ai[0] / sizeof ai[0][0][0]);
  printf("    ai[0][0]:%4i bytes; tiene:%3i elementos\n",
       sizeof ai[0][0], sizeof ai[0][0] / sizeof ai[0][0][0]);
  printf(" ai[0][0][0]:%4i bytes; tiene:%3i elementos\n",
    sizeof ai[0][0][0], sizeof ai[0][0][0] / sizeof ai[0][0][0]);
}

Salida:

Tamaño de ai: 120 bytes; tiene: 30 elementos
       ai[0]:  60 bytes; tiene: 15 elementos
    ai[0][0]:  12 bytes; tiene:  3 elementos
 ai[0][0][0]:   4 bytes; tiene:  1 elementos

§11.3  sizeof con matrices C

Las denominadas matrices "C" son conocidas también como NTBS ("Null Terminated Character String"), y como constantes de cadena ( 3.2.3f). Por tradición C, se identifican mediante un puntero a su primer carácter. Es decir, por un tipo char*, mientras que el final de cadena se identifica por el carácter nulo. Ejemplo:

char* cadena = "Cadena de caracteres";

En estas matrices no es posible utilizar el operador sizeof para determinar el tamaño del objeto-cadena, ya que la sentencia

std::cout << sizeof cadena;

siempre devolverá el tamaño del puntero (ver a continuación §12 ). En estos casos es posible utilizar la función strlen() de la Librería Estándar ( 5.5) para calcular la longitud (tamaño) de la matriz señalada:

std::cout << ::strlen(cadena);

Debe tener en cuenta que el resultado no incluye el carácter de fin de cadena, y que para un valor correcto se exige que la cadena no incluya caracteres nulos intermedios.


§12  Si el operando es un parámetro de tipo matriz o función, sizeof devuelve el tamaño del puntero. Ejemplo:

#include <iostream.h>

    void funci(char a[]);    // L.3:
int main(void) {             // ===============
   char arr[5] = {'A','E','I','O','U'};
   cout << "Tamaño matriz arr: " << sizeof arr << endl;
   funci(arr);
   return 0;
}
void funci(char arr[]) {     // L.10:
   cout << "Tamaño parametro arr: " << sizeof arr << endl;
}

Salida:

Tamaño matriz arr: 5
Tamaño parametro arr: 4

Comentario

La primera salida es la esperada; sin embargo, vemos que el tamaño del argumento arr pasado a la función aparece ser de 4 bytes; a pesar de que tanto el prototipo (L.3) como la propia definición de la función (L.10) lo anuncian como matriz de caracteres, y que sabemos que el tamaño de la matriz arr pasada es mayor. La explicación es que el compilador considera este argumento como un puntero al primer elemento de la matriz, y es justamente el tamaño de este puntero el valor que devuelve el operador ( 4.3.2).

§13  sizeof con estructura y uniones

Cuando se aplica a estructuras o uniones, el operador sizeof devuelve el total de bytes, incluyendo cualquier relleno o alineación interna de los datos ( 4.5.9a). Ejemplo:

#include <iostream.h>

int main(void) {         // ===============
   struct E1 { char c; float f; } e1;
   struct E2 { float f1; float f2; } e2;
   struct E3 { char c; int i; double d; } e3;

   cout << "Tamaño estr. E1: " << sizeof(E1) << endl;
   cout << "Tamaño estr. E2: " << sizeof(E2) << endl;
   cout << "Tamaño estr. E3: " << sizeof(E3) << endl;
}

Salida:

Tamaño estr. E1: 8
Tamaño estr. E2: 8
Tamaño estr. E3: 16

Comentario

Hemos visto en el ejemplo anterior , que la suma de tamaños de E1 (char y float) es: 1 + 4 == 5; sin embargo el tamaño obtenido es 8, lo que supone que hay un relleno de 3 bytes no utilizados [4].

En E2 el espacio teóricamente necesario (2 float) es: 4 + 4 == 8; vemos que en este caso el ajuste de los datos en memoria es exacto.

En E3 el espacio teórico para char, int y double sería: 1 + 4 + 8 == 13. Sin embargo, el programa informa que el tamaño de la estructura es de 16 bytes. El compilador vuelve a utilizar un relleno de 3 bytes.


§14  No se puede utilizar el operador sizeof con expresiones de tipo función; tipos incompletos; nombres parentizados (o tipos análogos), ni campos de bits ( 4.5.9). La imposibilidad de utilizar este operador con funciones, significa que está orientado a obtener el tamaño de objetos-dato, no de los algoritmos para manipulación de los datos [6].  Como veremos , esto tiene su trascendencia en el caso de utilizar el operador con clases, ya que en este tipo de objetos coexisten datos y código.

Ejemplo

#include <iostream.h>
void func() { cout << "Hola nundo" << endl; };
int main(void) {         // ===============
   struct E {
     float f1;
     int i1 : 4;
   } e;
   cout << "Tamaño estr. E: " << sizeof(E) << endl;    // L.9:  Ok.
   cout << "Tamaño de E.f1: " << sizeof E.f1 << endl;  // L.10: Error!!
   cout << "Tamaño de E.f1: " << sizeof e.f1 << endl;  // L.11: Ok.
   cout << "Tamaño de E.i1: " << sizeof e.i1 << endl;  // L.12: Error:
   cout << "Tamaño de func: " << sizeof func << endl;  // L.13: Error:
}

Salida después de eliminadas las sentencias erróneas:

Tamaño estr. E: 8
Tamaño de E.f1: 4

Comentario

En L.10 se recibe un error de compilación: Improper use of typedef 'E' in ....  La razón es que, si bien es posible determinar el tamaño de un tipo tal como E (en L.9), para determinar el de un miembro de una estructura o clase, es preciso referirse a una instancia concreta, cosa que se ha hecho sin novedad en L.11 (veremos que existe alguna excepción en esto ).

En L.12 se recibe un error: sizeof may not be applied to a bit field... que ya habíamos anunciado.

En L.13 también se recibe el error anunciado: sizeof may not be applied to a function...

§15  sizeof con clases

Debida a la especialísimas características de las clases, la utilización del operador sizeof con estos tipos requiere algunas consideraciones especiales. En principio son tratadas como cualquier otro tipo de dato (que en estos casos sean tipos definidos por el usuario, no tiene ninguna relevancia al respecto).

En anteriores epígrafes se han expuesto algunos ejemplos; naturalmente, la sintaxis empleada es distinta según se trate de un nombre-de-tipo o una instancia concreta.

Ejemplo

class Cl { long double ld; int i; } c;
class C2 { public: float x; float y; };
cout << sizeof(C1);       // L.1:
cout << sizeof c;         // L.2:
cout << sizeof(C2);


Naturalmente, los tamaños obtenidos en L.1 y L.2 son iguales. Recordar lo indicado al respecto de que sizeof solo considera el tamaño de los objetos-dato , y no el tamaño del código, por lo que solo tendrá en cuenta el tamaño de las propiedades de la clase. El tamaño de los métodos no se considera, aún en el caso que incluyan variables de cualquier tipo. Esto puede comprobarse con un sencillo experimento:

#include <iostream.h>

  class C1 {
    float fl;
    public: int in;
    float getf() {
      int x = 1;
      return fl+x;
    }
  };
  class C2 { float fl; int in; };

int main(void) {     // ===============

  C1 oc;

  cout << "Tamaño de oc: " << sizeof oc << endl;
  cout << "Tamaño de C1: " << sizeof(C1) << endl;
  cout << "Tamaño de C2: " << sizeof(C2) << endl;
}

Salida:

Tamaño de oc: 8
Tamaño de C1: 8
Tamaño de C2: 8

Se comprueba que el valor obtenido en los tres casos coincide con el valor teórico esperado, el tamaño de un float y un int ( 4 + 4 bytes). Observe que la existencia del int x en la función gerf es irrelevante (forma parte del código).

Nota: en realidad, los objetos C++ (instancias de clases) son una especie de estructuras (en el sentido C del término) en las que no están presentes las funciones miembro ni las variables o constantes estáticas (que tienen su existencia en el objeto-clase 4.11.5). Esta circunstancia es de la máxima importancia para algunas cuestiones. Por ejemplo, cuando se almacena en disco una instancia de clase no se almacenan (y recuperan) sus métodos, solo sus propiedades no-estáticas. También en la definición de punteros a propiedades no-estáticas. En el capítulo correspondiente ( 4.2.1g1) vimos que estos punteros no contienen una dirección determinada de memoria como es el caso en los punteros a variables "normales". En realidad, el puntero p a un miembro m de una clase C, contienen un valor que es el  "offset" o desplazamiento del miembro m en cuestión respecto de esta "estructura" de elementos. En consecuencia no tiene sentido hablar del objeto señalado ( *p ) o del valor ( p ) aislado de uno de estos punteros. Estos valores solo tienen significado cuando se refieren al miembro de un objeto c específico *(c.p), en cuyo caso el compilador obtiene la dirección concreta sumando el desplazamiento del p del miembro a la dirección de inicio del objeto.


§15.1   Tener en cuenta que también es válido para las clases lo indicado al tratar de sizeof con estructuras y uniones ( ). Cuando se aplica a clases, el operador sizeof devuelve el total de bytes de sus propiedades, incluyendo cualquier relleno o alineación interna de los datos.

Ejemplo:

#include <iostream.h>

int main() {        // ============
   class C1 { public: long double ld; int i; } c;
   class C2 { public: float x; float y; };
   cout << "Tamaño de C1: " << sizeof(C1) << endl;
   cout << "Tamaño de C2: " << sizeof(C2) << endl;
}

Salida:

Tamaño de C1: 16
Tamaño de C2: 8

Comentario

En el caso de C1, el tamaño teórico de sus dos propiedades, long double e int sería: 10 + 4 = 14, se ha producido un relleno de 2 bytes. En cambio para C2 el compilador no realiza ninguna alineación interna, ya que el valor obtenido coincide con el esperado para dos float ( 4 + 4).

No obstante lo anterior, tenga en cuenta que la presencia de funciones virtuales, motiva la existencia de un puntero especial en los objetos de la clase (vptr 1.4.4) que influye en el tamaño resultante. Ver un ejemplo en "Tabla de funciones virtuales"  ( 4.11.8a).


§15.2   Por lo general, la comprobación del tamaño de miembros de clases exige la existencia de un objeto (una instancia concreta de la clase), ya que (salvo excepciones) las propiedades no existen en la función-clase ( 4.11.5). Además, si la comprobación se va a realizar desde el exterior, se exige la adecuada accesibilidad del miembro.

Considere detenidamente el siguiente ejemplo y sus comentarios:

#include <iostream.h>

class Cl {
  long lg;
  static int is;
  public:
  static char ce;
  long double ld;
  int in;
  short sh;
  int getsize() { return sizeof lg; }
};

int main() {      // ===========
  Cl c1;
  cout << "Tamaño de Cl: " << sizeof(Cl) << endl;     // L.16 Ok.
  cout << "      Cl::ld: " << sizeof Cl::ld << endl;  // L.17 Error!
  cout << "       c1.lc: " << sizeof c1.ld << endl;   // L.18 Ok.
  cout << "      Cl::ce: " << sizeof Cl::ce << endl;  // L.19 Ok.
  cout << "      CL::is: " << sizeof Cl::is << endl;  // L.20 Error!
  cout << "       c1.lg: " << sizeof c1.lg << endl;   // L.21 Error!
  cout << "       c1.lg: " << c1.getsize() << endl;   // L.22 Ok.
  cout << "          c1: " << sizeof c1 << endl;      // L.23 Ok.
}

Salida (después de eliminadas sentencias erróneas):

Tamaño de Cl: 32
       c1.lc: 10
      Cl::ce: 1
       c1.lg: 4
          c1: 32

Comentario

En L.16  se obtiene 32 como tamaño de la clase.  Las propiedades de la clase son: long; int; char; long double; int y short. Lo que conduce a un valor teórico de: 4 + 4 + 1 + 10 + 4 + 2 == 25. Aparentemente existe un ajuste de 7 bytes, aunque este punto debe ser discutido más ampliamente. Ver a continuación .

L.17  Error.  El compilador nos avisa que no es posible acceder a una propiedad sin referirse a una instancia concreta:

Member Cl::ld cannot be used without an object....

L.18  Ok. en este caso se utiliza el objeto c1. El tamaño 10 es el esperado para un long double.

L.19  Ok. Se trata de un caso excepcional; puede accederse a la propiedad ce sin referirse a un objeto concreto y el resultado es el esperado para este dato (char). La razón es que se trata de un miembro estático, cuya existencia se localiza precisamente en el objeto-clase ( 4.11.7).

L.20  Se obtiene un nuevo error, el compilador indica 'Cl::is' is not accessible in .... Esta vez el acceso no es posible por falta de visibilidad del miembro desde el exterior. Lo mismo ocurre en L.21, donde se obtiene el mismo error por la misma causa, aunque esta vez no se refiere a una propiedad de clase, sino a una propiedad del objeto c1.ce.

En L.22 se obtiene el resultado correcto. El miembro privado de un objeto es accedido a través de un método público.

En L.23 comprobamos que el resultado de aplicar el operador sobre una clase como tipo de dato, sizeof ( Clase-C), es el mismo que si se aplica sobre una instancia de la clase, sizeof Objeto-C.


§15.3  Si compilamos dos versiones del ejemplo anterior haciendo primero todas las propiedades estáticas y después dinámicas, obtenemos los siguientes valores para la clase Cl:

Versión de la clase

Compilador

sizeof(Cl)

class Cl {
  static long lg;
  static int is;
  static char ce;
  static long double ld;
  static int in;
  static short sh;
}

  Borland C++  5.5

8

  MS Visual C++  6.0

1

class Cl {
  long lg;
  int is;
  char ce;
  long double ld;
  int in;
  short sh;
}

  Borland C++  5.5

40

  MS Visual C++  6.0

32

Tamaño teóricos de Cl en función de sus propiedades

  Borland C++ 5.5

25

  MS  Visual C++ 6.0

23

Puede verse que en ningún caso se obtiene el valor teórico esperado. Utilizando las opciones de alineación interna por defecto, ambos compiladores ( 4.5.9a) utilizan ajustes abultados; valores 40 y 32 frente a los teóricos de 25 y 23 respectivamente. El compilador Borland es el que utiliza un ajuste más elevado (15 bytes frente a los 9 de MS).

En cuanto a los sorprendentes resultados obtenidos para la versión en que todas las propiedades se declaran estáticas, la explicación es que, como hemos indicado, tales variables no existen realmente en el objeto, sino en el objeto-clase ( 4.11.5), y los compiladores proporcionan un resultado de sizeof pensado para informar el tamaño de objetos, por lo que no se cuentan los espacios de las propiedades estáticas.


§15.4
  La expresión  sizeof(classtype), donde classtype es una clase derivada de otra, devuelve el tamaño del objeto. Recuerde que esto incluye el tamaño total, incluyendo miembros privativos y heredados ( 4.11.2b), lo que permite afirmar (en sentido figurado) que incluye el tamaño de la superclase.


§15.5  Como ejemplo avanzado, considere el lector la utilización del operador sizeof en el constructor de una clase.

CWnd::CWnd() {
  memset(((b_class*)this)+1, 0, sizeof(*this) - sizeof(class b_class));
}

Este constructor de la clase CWnd, una de las principales de MFC [5], realmente hace poca cosa, solo poner a cero todos los bytes de cada variable miembro del objeto que se crea. Para ello utiliza memset, una función de la librería estándar C/C++ ( 5).

Para comprender el significado de los argumentos utilizados en el constructor considere que la definición de la función es:

#include <mem.h>
void* memset(void *s, int c, size_t n);

Que esta función hace que los n primeros bytes de la matriz (bloque de memoria) s sean iguales al carácter c, y el siguiente ejemplo:

char buffer[] = "Me convertiré en estrellas (*)\n";
...
memset(buffer, '*', strlen(buffer) - 1);

§16  size_t

El resultado producido (devuelto) por el operador sizeof es un entero sin signo denominado de forma estándar size_t, y cuya definición, que depende de la implementación, puede encontrarse en la cabecera <cstddef> [2]. En el caso de MS Visual C++ 6.0 y Borland C++, el tipo de size_t es unsigned int.

  Inicio.


[1]  Uno de los pocos casos en que el identificador de una matriz no es convertido en puntero al primer elemento.

[2]  Este es su "sitio" oficial, aunque si consulta, lo más probable es que lo redireccione a otro archivo de cabecera, por ejemplo a <stddef.h>.

[3]  Aquí estamos utilizando los tipos básicos como clases; por ejemplo, la clase de los long double; la variable ld sería una representación concreta (instancia) de dicha clase. En realidad los tipos básicos son clases con métodos (operadores) bien establecidos.

[4]  Utilizando la opción de alineación interna por defecto del compilador ( 4.5.9a).

[5]  MFC  "Microsoft Foundations Classes" ( 1.7.2).

[6]  Por conveniencia en el cálculo de punteros genéricos (void*) y punteros-a-función, el compilador GNU cpp considera el "sizeof" de una función y el de void como 1. No obstante, proporciona una opción de compilación (-Wpointer-arith) que avisa si se utiliza cualquier expresión que dependa del "size of" de una función o de void.