C语言 是否保证执行 memcpy(0,0,0) 是安全的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5243012/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it guaranteed to be safe to perform memcpy(0,0,0)?
提问by Matthieu M.
I am not so well-versed in the C standard, so please bear with me.
我不太精通 C 标准,所以请多多包涵。
I would like to know if it is guaranteed, by the standard, that memcpy(0,0,0)is safe.
我想知道按照标准是否可以保证memcpy(0,0,0)安全。
The only restriction I could find is that if the memory regions overlap, then the behavior is undefined...
我能找到的唯一限制是,如果内存区域重叠,则行为未定义......
But can we consider that the memory regions overlap here ?
但是我们可以认为内存区域在这里重叠吗?
采纳答案by templatetypedef
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 memcpythat
我有一个 C 标准的草案版本 (ISO/IEC 9899:1999),关于这个调用,它有一些有趣的事情要说。对于初学者来说,它提到的问候(§7.21.1/ 2)memcpy是
Where an argument declared as
size_tn 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.
在声明为
size_tn的参数指定函数数组的长度的情况下,n 在调用该函数时可以具有零值。除非在本小节中对特定函数的描述中另有明确说明,否则此类调用的指针参数仍应具有有效值,如 7.1.4 中所述。在这样的调用中,定位字符的函数没有找到,比较两个字符序列的函数返回零,复制字符的函数复制零个字符。
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.
如果函数的参数具有无效值(例如函数域外的值,或程序地址空间外的 指针,或空指针,或对应参数时指向不可修改存储的指针)不是 const 限定的)或具有可变数量参数的函数不期望的类型(升级后),行为是 undefined。
So it looks like according to the C spec, calling
所以它看起来像根据 C 规范,调用
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 memcpybroke 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.
也就是说,memcpy如果你这样做了,如果有任何实际的 break 实现,我会感到非常惊讶,因为如果你说复制零字节,我能想到的大多数直观实现都不会做任何事情。
回答by user1998586
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
只是为了好玩,gcc-4.9 的发行说明表明它的优化器使用了这些规则,例如可以删除条件
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).
然后在copy(0,0,0)调用时给出意想不到的结果(参见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.
我对 gcc-4.9 的行为有些矛盾;该行为可能符合标准,但能够调用 memmove(0,0,0) 有时是对这些标准的有用扩展。
回答by VonC
You can also consider this usage of memmoveseen in Git 2.14.x (Q3 2017)
您还可以考虑memmove在 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)
请参阅René Scharfe ( ) 的commit 168e635(2017 年 7 月 16 日)和commit 1773664、commit f331ab9、commit 5783980(2017 年 7 月 15 日)。(由Junio C Hamano合并-- --在commit 32f9025,2017 年 8 月 11 日)rscharfegitster
It uses an helper macro MOVE_ARRAYwhich calculates the size
based on the specified number of elements for us and supports NULLpointers when that number is zero.
Raw memmove(3)calls with NULLcan
cause the compiler to (over-eagerly) optimize out later NULLchecks.
它使用一个辅助宏MOVE_ARRAY,该宏根据指定的元素数量为我们计算大小,并NULL在该数字为零时支持指针。
原始memmove(3)调用 withNULL可能会导致编译器(过于急切地)优化以后的NULL检查。
MOVE_ARRAYadds 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 supportsNULLpointers iff 0 elements are to be moved.
MOVE_ARRAY添加了一个安全方便的助手,用于移动可能重叠的数组条目范围。
它推断元素大小,自动和安全地乘以以字节为单位的大小,通过比较元素大小进行基本的类型安全检查,不像memmove(3)它支持NULL指针 iff 0 元素将被移动。
#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);
It uses the macro BUILD_ASSERT_OR_ZEROwhich asserts a build-time dependency, as an expression (with @condbeing 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.
它使用断言构建时依赖项的宏BUILD_ASSERT_OR_ZERO作为表达式(@cond编译时条件必须为真)。
如果条件不成立,或者编译器无法评估,编译将失败。
#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))
回答by Kai Petzke
No, memcpy(0,0,0)is not safe. The standard library will likely not fail on that call. However in a testing envirenment, some extra code might be present in memcpy() to detect buffer overruns and other problems. And how that special version of memcpy() reacts to NULL pointers is, well, undefined.
不,memcpy(0,0,0)不安全。标准库可能不会在该调用中失败。然而,在测试环境中, memcpy() 中可能存在一些额外的代码来检测缓冲区溢出和其他问题。那个特殊版本的 memcpy() 对 NULL 指针的反应是,嗯,未定义。

