Чи можу я викликати memcpy () та memmove () з "кількістю байтів", встановлених на нуль?


102

Чи потрібно мені ставитися до випадків, коли мені фактично нічого рухати / копіювати з memmove()/ memcpy()як крайові випадки

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

або я повинен просто викликати функцію без перевірки

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

Чи потрібна перевірка в колишньому фрагменті?


6
питання нагадує мені трохи перевірити наявність нульових покажчиків на такі функції, як безкоштовна. Не потрібно, але я б помістив коментар, щоб показати, що ви думали про це.
Жаба

12
@Toad: Якої цілі це служить, окрім захаращування коду? Читаючи чийсь код, мені не потрібно знати, що оригінальний програміст "думав робити цю операцію, яка насправді не потрібна, але, оскільки це непотрібно, я цього не робив". Якщо я бачу звільнений вказівник, я знаю, що це може бути нульовим, тому мені не потрібно знати думок оригінального програміста на тему "чи слід перевіряти на нуль". І те саме стосується копіювання 0 байтів зmemcpy
jalf

8
@jalf: той факт, що це питання про stackoverflow, змушує людину сумніватися. Тож додавання коментаря може не допомогти вам, але може допомогти комусь із меншими знаннями
жаба

2
@Toad Так, коментарі відверто закликають, чому перевірка, яка виглядає необхідною, насправді не може бути цінною в принципі. Інша сторона медалі полягає в тому , що цей конкретний приклад є загальним випадком з участю стандартної бібліотечної функції , що кожен програміст потребує тільки дізнатися відповідь на один раз; тоді вони можуть визнати в будь-якій програмі, яку вони читають, що ці перевірки не потрібні. З цієї причини я пропускаю коментарі. База коду з декількома подібними дзвінками або потребуватиме копіювання та вставлення коментарів до кожного, або довільно використовувати їх лише для деяких дзвінків, обидва з яких некрасиві.
Марк Амері

Відповіді:


145

Зі стандарту 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 СТОП ДОПОМОГА ВІД

4
Про те, що це дійсний C, вже було охоплено іншими відповідями, як я вже сказав на самому початку своєї відповіді: "Як сказав @You, стандарт визначає, що memcpy та memmove повинні без проблем обробляти цей випадок". Я щойно додав свою думку з приводу того, що ви навіть не повинні боятися називати memcpy з len = 0 з міркувань продуктивності, оскільки в такому випадку це дзвінок з майже нульовою вартістю.
Маттео Італія
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.