Могу ли я вызвать memcpy () и memmove () с нулевым «количеством байтов»?

104

Нужно ли мне случаи лечат , когда я actully не имею ничего перемещать / копировать с memmove()/ memcpy()в крайних случаях

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

или мне просто вызвать функцию без проверки

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

Необходима ли проверка в предыдущем фрагменте?

острый зуб
источник
6
Вопрос немного напоминает мне проверку нулевых указателей на такие функции, как бесплатные. Не обязательно, но я бы поставил комментарий, чтобы показать, что вы думали об этом.
Toad
12
@Toad: Какой цели это служит, кроме как загромождать код? Читая чей-то код, мне не нужно знать, что исходный программист «думал о выполнении этой операции, в которой на самом деле нет необходимости, но, поскольку она не нужна, я этого не делал». Если я вижу, что указатель освобождается, я знаю, что ему разрешено иметь значение NULL, поэтому мне не нужно знать мысли исходного программиста по поводу «следует ли мне проверять наличие NULL». И то же самое касается копирования 0 байтов с помощьюmemcpy
jalf
9
@jalf: тот факт, что это вопрос о stackoverflow, заставляет людей сомневаться в этом. Так что добавление комментария может вам не помочь, но может помочь кому-то с меньшими знаниями,
Toad
2
@Toad Да, комментарии, явно указывающие на то, почему проверка, которая выглядит необходимой на самом деле, не может быть полезной в принципе. Другая сторона медали состоит в том , что этот конкретный пример является общим случаем с участием стандартной библиотечной функции , что каждый программист нуждается только узнать ответ на один раз; тогда они смогут распознать в любой прочитанной ими программе, что эти проверки не нужны. По этой причине я бы пропустил комментарии. Кодовая база с несколькими подобными вызовами должна либо копировать и вставлять комментарии к каждому из них, либо произвольно использовать их только для некоторых вызовов, оба из которых уродливы.
Марк Эмери

Ответы:

146

Из стандарта C99 (7.21.1 / 2):

Если аргумент, объявленный как, size_t nопределяет длину массива для функции,n может иметь нулевое значение при вызове этой функции. Если иное явно не указано в описании конкретной функции в этом подпункте, аргументы указателя при таком вызове должны по-прежнему иметь допустимые значения, как описано в 7.1.4. При таком вызове функция, которая определяет местонахождение символа, не находит вхождения, функция, сравнивающая две последовательности символов, возвращает ноль, а функция, копирующая символы, копирует ноль символов.

Итак, ответ - нет; проверка не обязательна (или да, можно пройти ноль).

Майк Сеймур
источник
1
Будет ли указатель считаться «действительным» для целей такой функции, если он указывает на место, следующее за последним элементом массива? Такой указатель не может быть законно принят, но можно безопасно делать некоторые другие вещи, похожие на указатель, например вычитать из него единицу.
supercat
1
@supercat: да, указатель, который указывает на один за концом массива, действителен для арифметики указателей с другими указателями внутри (или за одним концом) этого массива, но не может быть разыменован.
Майк Сеймур,
@MikeSeymour: Разве цитата не подразумевает противоположный ответ: проверка необходима, и вы не можете передать ноль с помощью нулевых указателей?
neverhoodboy
7
@ Neverhoodboy: Нет, в цитате четко сказано, что « nможет иметь нулевое значение». Вы правы, что вы не можете передавать нулевые указатели, но вопрос не об этом.
Майк Сеймур,
1
@MikeSeymour: Моя вина. Очень жаль. Вопрос в размере, а не в указателе.
neverhoodboy
5

Как сказал @You, стандарт определяет, что memcpy и memmove должны обрабатывать этот случай без проблем; поскольку они обычно реализуются как-то вроде

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

у вас даже не должно быть никаких штрафов за производительность, кроме вызова функции; если компилятор поддерживает встроенные функции / встраивание для таких функций, дополнительная проверка может даже сделать код чуть-чуть медленнее, поскольку проверка уже выполняется в это время.

Маттео Италия
источник
1
Я бы подумал, что эта функция, вероятно, сделана на ассемблере, где вы можете оптимизировать передачу памяти намного лучше, чем в c
Toad
"как-то похоже" :) На самом деле почти все реализации, которые я видел, находятся в сборке, и я пытаюсь скопировать большую часть бит, используя собственный размер слова (например, uint32_t на x86), но это не меняет сути ответа : это в то время как цикл , который не требует больших вычислений перед началом, так что проверка уже сделана.
Маттео Италия,
9
-1, типичная реализация не имеет отношения к тому, допустимо ли C вызывать эти функции (которые могут даже не быть реализованы как функции C) с нулевым аргументом.
R .. GitHub НЕ ПОМОГАЕТ ICE
4
Тот факт, что это действительный C, уже был охвачен другими ответами, как я сказал в самом начале своего ответа: «Как сказал @You, стандарт указывает, что memcpy и memmove должны обрабатывать этот случай без проблем». Я просто добавил свое мнение о том, что вам не следует даже бояться вызова memcpy с len = 0 по соображениям производительности, поскольку в этом случае это вызов с почти нулевой стоимостью.
Matteo Italia,