Я не так хорошо разбираюсь в стандарте C, поэтому, пожалуйста, потерпите меня.
Я хотел бы знать, гарантируется ли это по стандарту, что memcpy(0,0,0)
это безопасно.
Единственное ограничение, которое я смог найти, это то, что если области памяти перекрываются, то поведение не определено ...
Но можно ли считать, что области памяти здесь перекрываются?
c
memcpy
language-lawyer
null-pointer
Матье М.
источник
источник
memcpy(0,0,0)
это один из самых странных фрагментов кода C, который я видел.memcpy(outp, inp, len)
,? И что это могло произойти в коде, гдеoutp
иinp
размещаются динамически и изначально0
? Это работает, например, сp = realloc(p, len+n)
whenp
иlen
are0
. Я сам использовал такойmemcpy
вызов - хотя технически это UB, я никогда не сталкивался с реализацией, в которой он не запрещал бы операцию и никогда не ожидал бы этого.memcpy(0, 0, 0)
, скорее всего, предназначен для представления динамического, а не статического вызова ... т. е. значения этих параметров не обязательно должны быть литералами.Ответы:
У меня есть черновая версия стандарта C (ISO / IEC 9899: 1999), и я могу сказать несколько забавных вещей об этом вызове. Для начала, он упоминает (§7.21.1 / 2) в отношении
memcpy
этогоУказанная здесь ссылка указывает на это:
Итак, согласно спецификации C, вызов
memcpy(0, 0, 0)
приводит к неопределенному поведению, поскольку нулевые указатели считаются «недопустимыми значениями».
Тем не менее, я был бы крайне удивлен, если
memcpy
бы вы сделали это в какой-либо реальной реализации , поскольку большинство интуитивно понятных реализаций, о которых я могу думать, вообще ничего не сделали бы, если бы вы сказали копировать нулевые байты.источник
realloc(0, 0)
. Сценарии использования схожи, и я использовал их оба (см. Мой комментарий под вопросом). Бессмысленно и прискорбно, что Стандарт делает этот UB.Просто для удовольствия, примечания к выпуску для gcc-4.9 указывают, что его оптимизатор использует эти правила и, например, может удалить условное выражение в
int copy (int* dest, int* src, size_t nbytes) { memmove (dest, src, nbytes); if (src != NULL) return *src; return 0; }
который затем дает неожиданные результаты при
copy(0,0,0)
вызове (см. https://gcc.gnu.org/gcc-4.9/porting_to.html ).Я несколько двояко отношусь к поведению gcc-4.9; поведение может соответствовать стандартам, но возможность вызова memmove (0,0,0) иногда является полезным расширением этих стандартов.
источник
char *p = 0; int i=something;
, что оценка выражения(p+i)
приведет к неопределенному поведению, даже еслиi
оно равно нулю.memcpy()
должны быть разрешено выполнять любые арифметические операции над указателями на своих аргументах предшествующих обеспечению подсчета ненулевого другой вопрос [если я проектировал стандарты, я бы , вероятно , указать , что еслиp
имеет нулевое значение,p+0
может ловушка, ноmemcpy(p,p,0)
не буду делать ничего]. Гораздо более серьезная проблема, IMHO, - это неограниченность большинства неопределенных поведений. Хотя есть некоторые вещи, которые действительно должныfree(p)
p[0]=1;
) есть много вещей, которые должны быть указаны как дающие неопределенный результат (например, реляционное сравнение между несвязанными указателями не должно указываться как согласованное с любым другим сравнением, а должно быть указано как дающее либо 0 или 1), или должно быть указано как приводящее к более свободному поведению, чем определенное реализацией (компиляторы должны быть обязаны документировать все возможные последствия, например, целочисленного переполнения, но не указывать, какие последствия могут произойти в каждом конкретном случае).Вы также можете рассмотреть такое использование,
memmove
увиденное в Git 2.14.x (3 квартал 2017 г.)См. Commit 168e635 (16 июля 2017 г.) и зафиксировать 1773664 , зафиксировать f331ab9 , зафиксировать 5783980 (15 июля 2017 г.) Рене Шарфе (
rscharfe
) .(Объединено Junio C Hamano -
gitster
- в коммите 32f9025 , 11 августа 2017 г.)Он использует вспомогательный макрос,
MOVE_ARRAY
который вычисляет размер на основе указанного количества элементов для нас и поддерживаетNULL
указатели, когда это число равно нулю.Необработанные
memmove(3)
вызовы сNULL
могут заставить компилятор (чрезмерно) оптимизировать последующиеNULL
проверки.#define MOVE_ARRAY(dst, src, n) move_array((dst), (src), (n), sizeof(*(dst)) + \ BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src)))) static inline void move_array(void *dst, const void *src, size_t n, size_t size) { if (n) memmove(dst, src, st_mult(size, n)); }
Примеры :
- memmove(dst, src, (n) * sizeof(*dst)); + MOVE_ARRAY(dst, src, n);
Он использует макрос,
BUILD_ASSERT_OR_ZERO
который утверждает зависимость времени сборки, как выражение (с@cond
условием времени компиляции, которое должно быть истинным).Компиляция завершится ошибкой, если условие не выполняется или не может быть оценено компилятором.
#define BUILD_ASSERT_OR_ZERO(cond) \ (sizeof(char [1 - 2*!(cond)]) - 1)
Пример:
#define foo_to_char(foo) \ ((char *)(foo) \ + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
источник
Нет,
memcpy(0,0,0)
это небезопасно. Стандартная библиотека, скорее всего, не откажется от этого вызова. Однако в тестовой среде в memcpy () может присутствовать дополнительный код для обнаружения переполнения буфера и других проблем. И то, как эта специальная версия memcpy () реагирует на указатели NULL, в общем, не определено.источник