HOWTO · Arduino
Arduino memcpy e memmove
Pode-se copiar um bloco de memória de uma variável para outra usando as funções memcpy() e memmove().
Nesta página
Programas para Arduino podem usar as funções da biblioteca padrão de C memcpy() e memmove() para copiar bytes entre posições de memória. Escolha memcpy() quando as regiões de origem e destino não se sobrepõem; escolha memmove() quando puderem se sobrepor. Ambas as funções copiam a quantidade de bytes especificada por quem as chama, e nenhuma conhece o tamanho do buffer de destino. Por isso, o programa precisa garantir que haja espaço suficiente no destino.
Usar memcpy() para copiar bytes
A assinatura da função é void *memcpy(void *dest, const void *src, size_t n);. O argumento dest aponta para o primeiro byte a ser escrito, src aponta para o primeiro byte a ser lido e n indica uma quantidade de bytes. Não é uma contagem de elementos de um array. Para um array, sizeof(array) fornece seu tamanho total em bytes enquanto ele estiver no escopo; para um ponteiro, sizeof(pointer) fornece o tamanho do próprio ponteiro.
As regiões de origem e destino não podem se sobrepor. Se até mesmo um byte pertencer às duas regiões, chamar memcpy() resulta em comportamento indefinido: as regras de C e C++ não garantem nenhum resultado específico. Não é correto prever que a chamada sempre causará estouro ou corromperá dados de uma maneira determinada. Para uma cópia comum, use um buffer de destino separado e certifique-se de que ele comporte pelo menos n bytes graváveis. A função não aloca memória nem verifica a capacidade.
Usar memmove() com regiões sobrepostas
void *memmove(void *dest, const void *src, size_t n); recebe os mesmos argumentos e copia a mesma quantidade de bytes. Ao contrário de memcpy(), seu comportamento é definido quando as regiões de origem e destino se sobrepõem. O resultado é como se os bytes originais da origem fossem preservados primeiro e, em seguida, gravados no destino. Assim, deslocar parte de um array dentro do próprio array é um uso adequado.
Essa garantia trata apenas da sobreposição. memmove() ainda grava n bytes a partir de dest; a função não consegue determinar o tamanho do objeto apontado nem impedir uma gravação fora dos limites. Antes de chamá-la, confira o espaço disponível no destino e certifique-se de que a região de origem seja válida para n bytes.
Escolher entre memcpy() e memmove()
Use memcpy() quando souber que as duas regiões são separadas. Isso expressa diretamente o caso mais simples e pode permitir que a implementação use uma cópia otimizada para regiões sem sobreposição. Use memmove() sempre que houver possibilidade de sobreposição ou quando a relação entre as regiões for incerta. Não confie que uma plataforma específica fará uma chamada sobreposta a memcpy() parecer funcionar; as otimizações do compilador podem tornar o comportamento indefinido especialmente imprevisível.
Para qualquer uma das funções, determine primeiro a quantidade de bytes e a capacidade do destino. Ao copiar um array inteiro, use o valor de sizeof enquanto ele ainda for um array e dimensione o destino para comportar pelo menos essa quantidade de bytes. Se copiar elementos de um tipo diferente de char, unsigned char ou std::byte, multiplique a quantidade de elementos por sizeof(element) e certifique-se de que a operação seja apropriada para esse tipo. Essas funções copiam representações de objetos; não fazem conversão nem atribuição elemento a elemento.
Exemplo de Arduino: copiar um array
O programa C++ independente a seguir demonstra a cópia de um array para um buffer separado e o deslocamento de dados dentro de um único buffer. Ele usa as mesmas chamadas da biblioteca padrão disponíveis no código C++ de Arduino. Em uma placa, coloque a cópia em setup() e envie o destino ao Monitor Serial com Serial.println(); dimensione o destino para comportar os bytes copiados, incluindo o byte nulo terminador quando a intenção for imprimir a string como uma string C.
Execute este exemplo completo em C++ com um compilador padrão de C++:
O código e os rótulos ou saídas exibidos nesta seção permanecem em inglês.
#include <cstdio>
#include <cstring>
int main() {
const char source[] = "LED";
char destination[sizeof(source)] = {};
std::memcpy(destination, source, sizeof(source));
std::printf("%s\n", destination);
char buffer[] = "ABCDE";
std::memmove(buffer + 1, buffer, 4);
std::printf("%s\n", buffer);
return 0;
}
LED
AABCD
Aqui, destination tem exatamente o tamanho necessário para o array de origem, incluindo seu terminador nulo. A segunda cópia desloca quatro bytes uma posição para a direita dentro de buffer; como as regiões de destino e origem se sobrepõem, memmove() é a função adequada. Em um sketch de Arduino, inclua Arduino.h como de costume e substitua as chamadas de desktop a std::printf() por Serial.println() após Serial.begin(...). As chamadas de cópia de memória e os requisitos de dimensionamento continuam os mesmos.