25 de outubro de 2021

Do bit Ao Byte

Embarcados, Linux e programação

Como criar arrays dinâmicos 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

Passamos pelos ponteiros em C, que também funciona em C++. Depois vimos algumas manipulações e a criação de ponteiros em C++. Ainda faltam diversas coisas entre esses dois pontos, mas retomo a cada oportunidade de aplicar nos sketches. Nesse artigo veremos como criar arrays dinâmicos em C++.

Por que criar arrays dinâmicos em C++?

Esse artigo trata especificamente do assunto em C++ porque, como visto antes, vamos usar new para criá-lo, e new é exclusivo do C++. O framework do Arduino é C++, portanto é aplicável.

Quando temos um tamanho definido no programa, podemos simplesmente declarar a variável no tamanho e pronto. Como em muitas das vezes os recursos sobram nas atuais MCUs, criar um array com espaço sobressalente também é normal. Mas há casos que levamos os recursos ao limite, principalmente quando usamos (eca) bluetooth. Claro que tudo precisa ser planejado, não adianta ter um array dinâmico que em dado momento exceda o limite de memória, mas quando a memória está limitada e duas funcionalidades diferentes do código dependem de memória extra, essa pode ser uma condição na qual se aplique a solução. Um outro cenário é quando uma determinada função pode ou não ser executada. Seria um desperdício alocar arrays para um evento que pode ou não ocorrer, com por exemplo, uma probabilidade de 50,38%. É um tiro no escuro.

Com new, criamos o array em tempo de execução invés de uma alocação estática na compilação!

Criando um array dinâmico com new

No artigo do robô controlado por WiFi eu mostrei um array de array de char, vale a pena dar uma conferida. Esse procedimento é semelhante:

int *ponteiro = new int[30];

Criamos um bloco de memória de 10 inteiros. Não é mais fácil que com malloc()? O operador new retorna o endereço do primeiro setor de memória desse array. Quando terminar de usar a variável, “descarte-a” com delete. Particularmente acho C++ mais fácil de gerenciar memória. Só que sempre tenha em mente: Tudo deve seguir o padrão. O operador new foi chamado com [], portanto o delete deve apontar para o mesmo tipo:

delete [] ponteiro;

Vamos tentar transformar isso em “linguagem humana”.

Se fizermos:

int *ponteiro = new int;

Alocamos um ponteiro para 1 inteiro. O retorno de new será como para a alocação de 30 inteiros; ele só devolve o endereço inicial. Para excluir:

delete ponteiro;

Se chamarmos o operador delete como nesse último exemplo, mas em um array, somente o primeiro endereço das 30 alocações será excluída. Vai funcionar? O efeito é indefinido, conforme especifica o padrão ANSI/ISO, mas vai sobrar lixo na memória se não der erro. Esses resíduos de memória se acumulam na repetição de chamadas, e o nome dado para isso é “memory leak” – o terror dos programadores.

Outra coisa que pode acontecer é o array não ser alocado dentro de uma rotina e, ao final dela, usarmos fora das condicionais o delete para um ponteiro instanciado com, por exemplo, NULL. A boa notícia é que não há problema em chamar delete em uma alocação assim:

int *ponteiro = NULL;

Só que isso não pode ocorrer duas vezes seguidas, então não deixe o operador delete em execução frenética dentro da função loop() porque vai dar problema.

Vai uma dica: Se precisar saber como está a alocação (livre ou em uso), podemos chamar sizeof.

Usando o array criado dinamicamente com new

Já vimos como gastar memória, agora vamos ver como fazer uso disso, hum?

Bem, supondo que estejamos usando o recurso em um Arduino. O inteiro tem 2 bytes. Temos o endereço do primeiro inteiro, daí como caminhar para o segundo item do array? Vimos em outro artigo como caminhar pelos endereços diretamente, mas para acessar esse array dinâmico fazemos o mesmo procedimento utilizado para caminhar em um array em C: indicamos o índice!

cout << ponteiro[29]; //imprime o último item do array dinâmico que alocamos no exemplo mais acima

Aqui temos um teste:

#include <iostream>
#include <cstring>
#include <math.h>

using namespace std;

int main(){
    char *ponteiros = new char [7];
    strcpy(ponteiros,"teste\n\0");
    cout << ponteiros;

    return 0;
}

Que resulta em:

E como dito antes, podemos modificar pelo índice:

No próximo artigo relacionado veremos algo que causará um pouco de surpresa para quem não está habituado ao uso de ponteiros. Espero que esteja gostando da série e até a próxima!

Esse recurso de arrays dinâmicos em C++ e a linguagem aprendi de fato depois de ler o livro “C++ Primer Plus, que recomendo a todos. Para quem lê inglês, o link para compra do livro é esse.

 

Revisão: Ricardo Amaral de Andrade