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.