Posso chiamare memcpy () e memmove () con "numero di byte" impostato a zero?


103

Devo trattare i casi in cui in realtà non ho nulla da spostare / copiare con memmove()/ memcpy()come casi limite

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

o dovrei semplicemente chiamare la funzione senza controllare

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

È necessario il controllo nel precedente frammento?


6
domanda mi ricorda un po 'di controllare i puntatori nulli su funzioni come free. Non necessario, ma ci metterei un commento per far vedere che ci hai pensato.
Toad

12
@ Toad: a cosa serve, oltre a ingombrare il codice? Quando leggo il codice di qualcuno, non ho bisogno di sapere che il programmatore originale "pensava di fare questa operazione che in realtà non è necessaria, ma poiché non è necessaria, non l'ho fatta". Se vedo che un puntatore viene liberato, so che è consentito essere nullo, quindi non ho bisogno di conoscere i pensieri del programmatore originale sull'argomento "dovrei verificare la presenza di null". E lo stesso vale per la copia di 0 byte conmemcpy
jalf

9
@jalf: il fatto che sia una domanda su stackoverflow, lo rende qualcosa che le persone dubitano. Quindi l'aggiunta di un commento potrebbe non aiutarti, ma potrebbe aiutare qualcuno con meno conoscenze
Toad,

2
@ Toad Sì, i commenti che chiedono esplicitamente perché un controllo che sembra necessario non lo sia davvero può essere prezioso in linea di principio. L' altro lato della medaglia è che questo particolare esempio è un caso comune che coinvolge una funzione di libreria standard a cui ogni programmatore deve imparare la risposta solo una volta; quindi possono riconoscere in qualsiasi programma che leggono che questi controlli non sono necessari. Per questo motivo, ometterei i commenti. Una base di codice con più chiamate come questa dovrà copiare e incollare i commenti su ciascuna di esse, oppure utilizzarle arbitrariamente solo su alcune chiamate, entrambe brutte.
Mark Amery

Risposte:


145

Dallo standard C99 (7.21.1 / 2):

Dove un argomento dichiarato come size_t nspecifica la lunghezza dell'array per una funzione, npuò avere il valore zero in una chiamata a quella funzione. A meno che non sia esplicitamente dichiarato diversamente nella descrizione di una particolare funzione in questo sottoclausola, gli argomenti del puntatore su tale chiamata devono ancora avere valori validi, come descritto in 7.1.4. In una tale chiamata, una funzione che individua un carattere non trova alcuna occorrenza, una funzione che confronta due sequenze di caratteri restituisce zero e una funzione che copia i caratteri copia zero caratteri.

Quindi la risposta è no; il controllo non è necessario (o sì; puoi passare lo zero).


1
Un puntatore sarebbe considerato "valido" ai fini di tale funzione se puntasse alla posizione che segue l'ultimo elemento di un array? Un tale puntatore non può essere legittimamente differito, ma si potrebbero tranquillamente fare altre cose da puntatore come sottrarre uno da esso.
supercat

1
@supercat: sì, un puntatore che punta uno oltre la fine di un array è valido per l'aritmetica dei puntatori con altri puntatori all'interno (o uno oltre la fine di) quell'array, ma non è dereferenziabile.
Mike Seymour

@ MikeSeymour: la citazione non dovrebbe implicare una risposta opposta: il controllo è necessario e non puoi passare lo zero con puntatori nulli?
neverhoodboy

7
@neverhoodboy: No, la citazione dice chiaramente " npuò avere il valore zero". Hai ragione sul fatto che non puoi passare puntatori nulli, ma non è quello su cui si pone la domanda.
Mike Seymour

1
@ MikeSeymour: colpa mia. Veramente dispiaciuto. La domanda riguarda le dimensioni, non il puntatore.
neverhoodboy

5

Come detto da @You, lo standard specifica che memcpy e memmove dovrebbero gestire questo caso senza problemi; poiché di solito sono implementati in qualche modo come

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;
}

non dovresti nemmeno avere alcuna penalità nelle prestazioni oltre alla chiamata di funzione; se il compilatore supporta intrinsics / inlining per tali funzioni, il controllo aggiuntivo può anche rendere il codice un po 'più lento, poiché il controllo è già fatto nel frattempo.


1
Penso che questa funzione sia probabilmente realizzata in assembly in cui è possibile ottimizzare i trasferimenti di memoria molto meglio che in c
Toad

"in qualche modo come" :) In realtà quasi tutte le implementazioni che ho visto sono in assembly e provo a copiare la maggior parte dei bit usando la dimensione nativa della parola (es. uint32_t su x86), ma ciò non cambia la sostanza della risposta : è un ciclo while che non necessita di grandi calcoli prima di iniziare, quindi il controllo è già fatto.
Matteo Italia

9
-1, l'implementazione tipica è irrilevante per stabilire se sia valido C chiamare queste funzioni (che potrebbero non essere nemmeno implementate come funzioni C) con un argomento zero.
R .. GitHub SMETTA DI AIUTARE IL GHIACCIO

4
Il fatto che sia C valido è già stato coperto dalle altre risposte, come ho detto all'inizio della mia risposta: "Come detto da @You, lo standard specifica che memcpy e memmove dovrebbero gestire questo caso senza problemi". Ho solo aggiunto la mia opinione sul fatto che non dovresti nemmeno aver paura di chiamare memcpy con len = 0 per motivi di prestazioni, poiché in quel caso è una chiamata con un costo quasi zero.
Matteo Italia
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.