Alocação de memória em C++

arrays dinâmicos em C++ | Interagir com ponteiros | Alocação de memória | Ponteiros em C/C++ | Socket server com ESP8266 | Socket server com ESP32 | Socket server com Python | Socket client com ESP32 | Sistema de arquivos no ESP8266

Antes de entrar no tema, preciso deixar claro que esse artigo sobre alocação de memória em C++ é uma continuação do artigo de ponteiros em C/C++, iniciado em uma lacuna entre os artigos agendados, razão pela qual a continuação demorou tanto para sair. E na verdade eu escrevi os dois no mesmo dia, só não quis publicá-los no mesmo dia por questão de aproveitamento e prazo para fazer vídeo. Continuando então de onde paramos.

Cuidados ao usar ponteiros para alocação de memória

Quando alocamos um ponteiro, reservamos um ponto de partida para outro endereço contendo os dados, não alocamos o espaço para esses dados. A criação do espaço de alocação é outra etapa do processo e deixar de fazê-lo acabará resultando em anomalias indetectáveis e aleatórias. Por essa razão, quando estiver desenvolvendo algo que envolva uma lógica com ponteiros tente experimentar  em uma função à parte e tente criar testes unitários ou algo parecido.

Um exemplo de alocação no limbo:

#include <iostream>
#include <cstring>

using namespace std;

int main(){

    uint8_t tamanho = 7;
    cout << "exemplo 03" << endl;
   
    char *myValue = (char *) malloc(tamanho * sizeof(char));

    for (uint8_t i=0; i<7;i++){
        cin >>myValue[i];
    }

    cout << myValue << endl;

    int *teste;
    *teste = 12345;
    cout << teste << endl;

    return 0;
}

O programa compilará sem erros nem avisos. Também executará, recolherá os valores de entrada mas, ao chegar no ponteiro teste as coisas ficarão estranhas; ou será exibido um valor estranho, ou haverá uma invasão de memória, ou pior: não acontecerá nada até que aconteça em um determinado fluxo. E esse tipo de problema é difícil depurar se for erro de lógica como esse.

Alocação de memória

Um ponteiro sempre descreve um endereço, portanto é um tipo de inteiro que não se utiliza para operações matemáticas. Um ponteiro para um inteiro sim.

Alocação de memória com new

Em C++ a alocação de memória é menos trabalhosa e mais descomplicada. Claro, o método do artigo de referência funciona em ambos, afinal, C++ é uma “carenagem” pro C (programadores C++ odeiam esse tipo de analogia).

int *ponteiro = new int;

Estamos dizendo que new deve alocar memória para um tipo int. A memória é reservada e o endereço é devolvido. A diferença é que agora para acessar a variável devemos referenciar o ponteiro, enquanto que alocando como exemplificado no primeiro artigo, acessamos pelo nome da variável. No C++ essa alocação resulta em um “objeto de dados”. Não devemos confundir isso como um “objeto” de orientação da linguagem; é mais para “uma coisa”, não simplesmente um número.

Para um único objeto de dados, a forma geral é essa:

<tipo> <nome> = <new tipo>

Um exemplo de uso é melhor que um exemplo teórico, então vamos lá:

Alocação de memória exibindo valor e endereço

Primeiro exibimos o valor atribuído, depois o endereço do ponteiro. No código fica assim:

#include <iostream>

using namespace std;

int main(){
    cout << "exemplo 04" << endl;
   
    int *ponteiro = new int;
    *ponteiro = 100;

    cout << "inteiro: ";
    cout << *ponteiro << endl;
    cout << "endereco: ";
    cout << ponteiro << endl;

    
    return 0;
}

Se não houver espaço para alocar memória, new retornará NULL (ou 0, se preferir).

Delete

Uma regra em C++ é: toda a alocação de memória feita com new, deve ser eliminado com delete. Se a memória não for liberada, esses “resíduos” de memória formarão o chamado “memory leaks”. Para embarcados isso é mais significativo, pois devido aos recursos limitados, a memória tenderá a se esgotar prematuramente. Pra piorar, dispositivos embarcados tendem a ficar ligados continuamente, nem dá pra contar com reinicializações programadas.

Seguindo a regra, é simples gerenciar a memória:

int *ponteiro = new int;
//resto do programa...
//...finalizou? Então...
delete ponteiro;

Isso não removerá o ponteiro, mas a memória para qual o ponteiro aponta, sendo possível reutilizá-lo para uma nova alocação com new. Uma vez liberada a memória, o processo delete não deve se repetir. O resultado pode ser qualquer coisa inesperada.

delete só deve ser utilizado com alocações criadas com new. Isso não funcionará:

int valor = 7;
int *ponteiro = &valor;
delete ponteiro;

Referência online

Como citei no primeiro artigo, tenho alguns livros de C++, sendo o meu preferido C++ Primer Plus, mas é leitura em inglês. Se inglês não é problema para você, recomendo fortemente. Para consultas rápidas e/ou específicas, uma excelente opção é o site cplusplus, nesse link.

Vídeo

Ainda tem material para mais uns 10 artigos sobre ponteiros. Os escreverei em largos intervalos para que não seja tedioso para vocês nem para mim.

O vídeo demonstrativo em nosso canal está disponível na playlist Programação. Se não é inscrito no canal ainda, inscreva-se, deixe seu like se gostar e comente!

Espero que estejam gostando do conteúdo!

 

Revisão: Ricardo Amaral de Andrade