是否保证执行memcpy(0,0,0)是安全的?

我对C标准不太了解,请耐心等待。

我想知道是否可以保证memcpy(0,0,0)是安全的。

唯一的限制,我可以find的是,如果内存区域重叠,那么行为是未定义的…

但是我们可以认为这里的内存区域是重叠的吗?

我有一个C标准草案(ISO / IEC 9899:1999),对这个呼叫有一些有趣的东西要说。 对于初学者来说,它提到了memcpy (§7.21.1/ 2)

如果声明为size_t n的参数指定函数的数组长度,那么在调用该函数时,n的值可以为零。 除非本小节中对特定函数的描述中另有明确说明,否则这种调用的指针参数仍应具有有效值,如7.1.4所述 。 在这样的调用中,find字符的函数没有发生,比较两个字符序列的函数返回0,而复制字符的函数复制零个字符。

这里所指的参考文献指出:

如果一个函数的参数有一个无效的值(比如函数的域之外的值,或者程序的地址空间之外的指针 ,或者空指针 ,或者指向相应参数的不可修改存储的指针不是const限定的)或者是一个types(在提升之后),如果函数的参数数量是可变的, 那么这个行为是不确定的

所以看起来像根据C规范,打电话

 memcpy(0, 0, 0) 

导致未定义的行为,因为空指针被认为是“无效值”。

也就是说,如果你真的执行了memcpy话,我会非常惊讶的,因为如果你说要复制零字节的话,我能想到的大部分直观的实现都不会做任何事情。

只是为了好玩,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)有时是这些标准的有用扩展。

你也可以考虑在Git 2.14.x(2017年第3季度)

请参阅提交168e635 (2017年7月16日),并提交1773664 , 提交f331ab9 , 提交5783980 (2017年7月15日) RenéScharfe( rscharfe ) 。
(由Junio C gitster合并- gitster -在2017年8月11日的gitster 提交 )

它使用一个辅助macrosMOVE_ARRAY ,它根据我们指定的元素数量来计算大小,当这个数字为零时,它支持NULL指针。
原始的memmove(3)调用NULL可以导致编译器(过分急切地)优化以后的NULL检查。

MOVE_ARRAY添加一个安全和方便的助手来移动可能重叠的数组项。
它推断元素的大小,自动和安全地乘以字节大小,通过比较元素的大小进行基本的types安全检查,而不像memmove(3)它支持NULL指针,如果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); 

它使用macrosBUILD_ASSERT_OR_ZERO作为expression式(其中@cond是编译时条件,必须为真)来声明构build时依赖关系。
如果条件不正确或编译器无法评估,编译将失败。

 #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))