Is it guaranteed to be safe to perform memcpy(0,0,0)?
I am not so well-versed in the C standard, so please bear with me.
I would like to know if it is guaranteed, by the standard, that memcpy(0,0,0)
is safe.
The only restriction I could find is that if the memory regions overlap, then the behavior is undefined...
But can we consider that the memo开发者_JAVA百科ry regions overlap here ?
I have a draft version of the C standard (ISO/IEC 9899:1999), and it has some fun things to say about that call. For starters, it mentions (§7.21.1/2) in regards to memcpy
that
Where an argument declared as
size_t
n specifies the length of the array for a function, n can have the value zero on a call to that function. Unless explicitly stated otherwise in the description of a particular function in this subclause, pointer arguments on such a call shall still have valid values, as described in 7.1.4. On such a call, a function that locates a character finds no occurrence, a function that compares two character sequences returns zero, and a function that copies characters copies zero characters.
The reference indicated here points to this:
If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined.
So it looks like according to the C spec, calling
memcpy(0, 0, 0)
results in undefined behavior, because null pointers are considered "invalid values."
That said, I would be utterly astonished if any actual implementation of memcpy
broke if you did this, since most of the intuitive implementations I can think of would do nothing at all if you said to copy zero bytes.
Just for fun, the release-notes for gcc-4.9 indicate that its optimizer makes use of these rules, and for example can remove the conditional in
int copy (int* dest, int* src, size_t nbytes) {
memmove (dest, src, nbytes);
if (src != NULL)
return *src;
return 0;
}
which then gives unexpected results when copy(0,0,0)
is called (see https://gcc.gnu.org/gcc-4.9/porting_to.html).
I am somewhat ambivalent about the gcc-4.9 behaviour; the behaviour might be standards compliant, but being able to call memmove(0,0,0) is sometimes a useful extension to those standards.
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 unlikememmove(3)
it supportsNULL
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))
精彩评论