Posso chamar memcpy () e memmove () com “número de bytes” definido como zero?

102

Preciso tratar casos quando, na verdade, não tenho nada para mover / copiar com memmove()/ memcpy()como casos extremos

int numberOfBytes = ...
if( numberOfBytes != 0 ) {
    memmove( dest, source, numberOfBytes );
}

ou devo apenas chamar a função sem verificar

int numberOfBytes = ...
memmove( dest, source, numberOfBytes );

A verificação do snippet anterior é necessária?

dente afiado
fonte
6
question me lembra um pouco da verificação de ponteiros nulos em funções como free. Não é necessário, mas gostaria de colocar um comentário lá para mostrar que você pensou sobre isso.
sapo
12
@Toad: a que propósito isso serve, além de bagunçar o código? Ao ler o código de alguém, não preciso saber que o programador original "pensou em fazer essa operação que não é realmente necessária, mas por ser desnecessária, não fiz". Se eu vir um ponteiro sendo liberado, sei que pode ser nulo, então não preciso saber o que o programador original pensou sobre o assunto "devo verificar se há nulo". E o mesmo vale para copiar 0 bytes commemcpy
jalf de
8
@jalf: o fato de ser uma pergunta sobre stackoverflow, faz com que as pessoas duvidem. Portanto, adicionar um comentário pode não ajudar você, mas pode ajudar alguém com menos conhecimento
Toad
2
@Toad Sim, comentários explicitamente explicando por que um cheque que parece necessário realmente não é podem ser valiosos em princípio. O outro lado da moeda é que este exemplo particular é um caso comum envolvendo uma função de biblioteca padrão para a qual cada programador só precisa aprender a resposta uma vez; então, eles podem reconhecer em qualquer programa que leiam que essas verificações não são necessárias. Por esse motivo, omitiria os comentários. Uma base de código com várias chamadas como essa precisará copiar e colar os comentários em cada uma ou usá-los arbitrariamente em apenas algumas chamadas, ambas feias.
Mark Amery

Respostas:

145

Do padrão C99 (7.21.1 / 2):

Onde um argumento declarado como size_t nespecifica o comprimento da matriz para uma função, npode ter o valor zero em uma chamada para essa função. A menos que explicitamente declarado de outra forma na descrição de uma função particular nesta subseção, os argumentos de ponteiro em tal chamada ainda devem ter valores válidos, conforme descrito em 7.1.4. Em tal chamada, uma função que localiza um caractere não encontra nenhuma ocorrência, uma função que compara duas sequências de caracteres retorna zero e uma função que copia caracteres copia zero caracteres.

Portanto, a resposta é não; a verificação não é necessária (ou sim; você pode passar zero).

Mike Seymour
fonte
1
Um ponteiro seria considerado "válido" para os propósitos de tal função se apontasse para o local após o último elemento de um array? Esse ponteiro não poderia ser legitimamente adiado, mas alguém poderia fazer com segurança outras coisas semelhantes a um ponteiro, como subtrair um dele.
supercat de
1
@supercat: sim, um ponteiro que aponta um após o final de um array é válido para aritmética de ponteiros com outros ponteiros dentro (ou após o final) desse array, mas não é desreferenciável.
Mike Seymour de
@MikeSeymour: A citação não deveria implicar em uma resposta oposta: a verificação é necessária e você não pode passar zero com ponteiros nulos?
neverhoodboy de
7
@neverhoodboy: Não, a citação diz claramente " npode ter o valor zero". Você está correto ao dizer que não pode passar ponteiros nulos, mas não é sobre isso que a pergunta está perguntando.
Mike Seymour
1
@MikeSeymour: Minha culpa. Sinceras desculpas. A questão é sobre o tamanho, não o ponteiro.
neverhoodboy de
5

Conforme dito por @You, o padrão especifica que o memcpy e o memmove devem lidar com este caso sem problemas; uma vez que geralmente são implementados de alguma forma, como

void *memcpy(void *_dst, const void *_src, size_t len)
{
    unsigned char *dst = _dst;
    const unsigned char *src = _src;
    while(len-- > 0)
        *dst++ = *src++;
    return _dst;
}

você não deve ter nenhuma penalidade de desempenho além da chamada de função; se o compilador suportar intrínsecos / inlining para tais funções, a verificação adicional pode até tornar o código um micro-bit mais lento, uma vez que a verificação já é feita no momento.

Matteo Italia
fonte
1
Eu acho que esta função provavelmente é feita em assembly onde você pode otimizar as transferências de memória muito melhor do que em c
Toad
"de alguma forma, como" :) Na verdade, quase todas as implementações que vi estão em montagem e tente copiar a maioria dos bits usando o tamanho da palavra nativa (por exemplo, uint32_t em x86), mas isso não muda a substância da resposta : é um enquanto loop que não precisa de grandes cálculos antes de iniciar, assim que o cheque já está feito.
Matteo Italia de
9
-1, a implementação típica é irrelevante para saber se é válido C chamar essas funções (que podem nem mesmo ser implementadas como funções C) com um argumento zero.
R .. GitHub PARAR DE AJUDAR ICE
4
O fato de ser C válido já foi coberto pelas outras respostas, como eu disse no início da minha resposta: "Como disse @You, o padrão especifica que o memcpy e o memmove devem lidar com este caso sem problemas". Acabei de acrescentar minha opinião sobre o fato de que você não deve nem ter medo de chamar memcpy com len = 0 por questões de performance, já que nesse caso é uma chamada com custo quase zero.
Matteo Italia de