2010-09-20 12 views
70

ho bisogno per il trattamento di casi in cui ho actully ho nulla da spostare/copiare con memmove()/memcpy() come casi limitePosso chiamare memcpy() e memmove() con "numero di byte" impostato su zero?

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

o devo solo chiamare la funzione senza controllare

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

è la verifica nel precedente frammento necessario?

+6

domanda mi ricorda un po 'di controllo per i puntatori nulli su funzioni come libero. Non necessario, ma vorrei mettere un commento lì per mostrarvi il pensiero su di esso. – Toad

+10

@Toad: quale scopo serve, se non quello di ingombrare il codice?Quando leggo il codice di qualcuno, non ho bisogno di sapere che il programmatore originale "ha pensato di fare questa operazione che non è effettivamente necessaria, ma perché non è necessaria, non l'ho fatto". Se vedo un puntatore essere liberato, * so * è permesso essere nullo, quindi non ho bisogno di conoscere i pensieri del programmatore originale sull'argomento "dovrei controllare per null". E lo stesso vale per la copia di 0 byte con 'memcpy' – jalf

+5

@jalf: il fatto che sia una domanda sullo stackoverflow, lo rende qualcosa che la gente dubita. Quindi aggiungere un commento potrebbe non aiutarti, ma potrebbe aiutare qualcuno con meno conoscenza – Toad

risposta

102

Dalla standard C99 (7.21.1/2):

Dove un argomento dichiarato size_t n specifica la lunghezza della matrice per una funzione , n può avere il valore zero in una chiamata a tale funzione. A meno che non sia esplicitamente indicato nella descrizione di una particolare funzione in questo sottopunto, gli argomenti del puntatore in tale chiamata devono ancora avere valori validi, come descritto in 7.1.4. In 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 zero).

+0

Un puntatore può essere considerato "valido" ai fini di tale funzione se puntava alla posizione che segue l'ultimo elemento di una matrice? Un puntatore del genere non può essere deferenziato in modo legittimo, ma si potrebbero tranquillamente fare altre cose da puntatore, come sottrarre uno da esso. – supercat

+0

@supercat: sì, un puntatore che punta oltre la fine di un array è valido per l'aritmetica del puntatore con altri puntatori all'interno (o oltre la fine) di tale array, ma non è dereferenziabile. –

+0

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

2

No, il controllo non è necessario. Affidarsi al fatto che lo zero sia gestito correttamente va bene e molto ragionevole, secondo me. Potrebbe valere la pena commentare.

+0

Non necessariamente. Un utilizzo molto comune sarebbe l'inserimento e la cancellazione da array che potrebbero non essere array 'char'. Qualsiasi utilizzo dell'array potrebbe avere una traslazione a lunghezza zero come un caso naturale ed è bello non doverlo fare in casi speciali. –

12

La documentazione di memmove e memcpy dice questo:

La funzione non verifica per qualsiasi carattere nullo di terminazione in fonte - passa sempre copie esattamente num byte.

Il opengroupdocumentation dice sostanzialmente la stessa cosa.

Quindi, dato che le copie "esattamente num byte", copierà zero byte quando num = 0, e quindi non dovrebbe essere necessario trattare questo come un caso speciale.

+1

Se copia sempre esattamente * num * byte, non avrà alcun problema a copiare esattamente 0 byte. :) –

+1

@qrdl: Sì, risponde alla domanda "devo trattare' num = 0' come caso speciale? ". – You

+1

L'OP ha chiesto informazioni sulla condizione del bordo, quindi dare la risposta di caso comune non è appropriato. 'num = 0' bit apparso dopo la modifica. – qrdl

5

Come detto da @You, lo standard specifica che memcpy e memmove devono gestire questo caso senza problemi; dato che 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 si dovrebbe avere alcuna penalità prestazionale diversa dalla chiamata di funzione; se il compilatore supporta intrinseche/inlining per tali funzioni, la verifica aggiuntiva potrebbe addirittura rendere il codice un micro-bit-bit più lento, poiché il controllo è già stato eseguito nel frattempo.

+0

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

+0

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

+5

-1, l'implementazione tipica è irrilevante sul fatto che sia C valido chiamare queste funzioni (che potrebbero non essere implementate anche come funzioni C) con un argomento zero. –