You can also consider this usage of memmove
seen in Git 2.14.x (Q3 2017)
See commit 168e635 (16 Jul 2017), and commit 1773664, commit f331ab9, commit 5783980 (15 Jul 2017) by René Scharfe (rscharfe
).
(Merged by Junio C Hamano -- gitster
-- in commit 32f9025, 11 Aug 2017)
It uses an helper macro MOVE_ARRAY
which calculates the size
based on the specified number of elements for us and supports NULL
pointers when that number is zero.
Raw memmove(3)
calls with NULL
can
cause the compiler to (over-eagerly) optimize out later NULL
checks.
MOVE_ARRAY
adds a safe and convenient helper for moving potentially overlapping ranges of array entries.
It infers the element size, multiplies automatically and safely to get the size in bytes, does a basic type safety check by comparing element sizes and unlike memmove(3)
it supports NULL
pointers iff 0 elements are to be moved.
#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));
}
Examples:
- memmove(dst, src, (n) * sizeof(*dst));
+ MOVE_ARRAY(dst, src, n);
It uses the macro BUILD_ASSERT_OR_ZERO
which asserts a build-time dependency, as an expression (with @cond
being the compile-time condition which must be true).
The compilation will fail if the condition isn't true, or can't be evaluated by the compiler.
#define BUILD_ASSERT_OR_ZERO(cond) \
(sizeof(char [1 - 2*!(cond)]) - 1)
Example:
#define foo_to_char(foo) \
((char *)(foo) \
+ BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
As noted by user16217248 in the comments:
In C11 the BUILD_ASSERT_OR_ZERO
macro is not necessary because we have static_assert()
memcpy
. – Chiapasmemcpy(0,0,0)
is one of the weirdest pieces of C code I've seen. – Selfemployedmemcpy(outp, inp, len)
? And that this could occur in code whereoutp
andinp
are dynamically allocated and are initially0
? This works, e.g., withp = realloc(p, len+n)
whenp
andlen
are0
. I myself have used such amemcpy
call -- while it is technically UB, I've never encountered an implementation where it isn't a no-op and don't ever expect to. – Shelfmemcpy(0, 0, 0)
is most likely intended to represent a dynamic, not static invocation ... i.e., those parameter values need not be literals. – Shelf