quarta-feira, 3 de agosto de 2011

A mágica dos arrays alocados dinamicamente

Vamos alocar um array de um objeto X da seguinte forma:

X* meuArrayX = new X[10];

Como descobrir depois, em tempo de execução, quantos objetos de "X" foram alocados em "meuArrayX"?

"É impossível!" - você diz.

Sim, sabemos que é impossível. Aprendemos isso na escola. Os livros dizem que é impossível. Mas...

Suponha que seu objeto X seja:

class X
{
public:
    X(){}
    ~X(){ printf("Destrutor\n" ); }
};

E em seu código você faz:

X* meuArrayX = new X[10];
delete[] meuArrayX;

Uau! Ele imprimiu 10x a palavra "Destrutor"!

Então, de alguma forma, o programa sabe, em tempo de execução, quantos objetos foram alocados. Qual será a mágica usada pelo delete[]?

Realmente, isso é uma mágica. Não está documentado em lugar nenhum. Porém, com um pouco de engenharia reversa, é possível descobrir o que o programa faz.

Vou revelar o truque que é utilizado pelos dois principais compiladores: o gcc e o Ms Visual Studio.

O que eles fazem é simplesmente alocar, antes do primeiro elemento do array, um inteiro que guarda a quantidade de elementos criados. Quer ver?

#include <cstdio>

class X
{
public:
    X(){}
    ~X(){ printf("Destrutor\n" ); }
};

int main()
{
    X* meuArrayX = new X[20];
    //criamos um ponteiro de int que aponta para os 4 bytes
    //anteriores ao meuArrayX[0]
    int* p = (int*)( (char*)&meuArrayX [0] - 4 );

    printf("Tamanho do array: %d\n", *p );

    delete[] meuArrayX;

    getchar();
    return 0;
}

E esta é a mágica ;)

Lembrando que esta solução não é documentada, não é portável, não está na ISO do C++ e não deve ser utilizada por ninguém (como quase tudo deste blog).

Nenhum comentário:

Postar um comentário