这个问题是关于memcpy(0, 0, 0)
定义性的,它已经被确定为未定义行为,这与先前的问题有关。
正如链接的问题所示,答案取决于C11条款7.1.4:1的内容:
除非在随后的详细说明中明确说明,否则将适用以下每个语句:如果函数参数具有无效值(如函数域外的值或程序地址空间外的指针或空指针[...]),则行为未定义。[...]
标准函数memcpy()
期望指向void
和const void
的指针,如下所示:
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
这个问题的提出是有意义的,因为标准中存在两种“有效”指针的概念:一种是可以通过指针算术运算得到并可以与同一对象内的其他指针进行有效比较的指针;另一种是用于解引用的指针。前者包括“过界”指针,例如以下代码片段中的
&a + 1
和&b + 1
,而后者不包括这些指针作为有效指针。char a;
const char b = '7';
memcpy(&a + 1, &b + 1, 0);
考虑到memcpy()
的参数被定义为指向void
的指针,因此它们的有效性问题不能涉及对它们进行解引用操作。那么上述代码片段应该被视为已定义行为吗?或者&a + 1
和&b + 1
应该被视为“程序地址空间之外”?
这对我很重要,因为我正在规范标准C函数的影响。我曾将memcpy()
的一个前置条件写成requires \valid(s1+(0 .. n-1));
,直到有人指出我的注意,即GCC 4.9已经开始积极地优化此类库函数调用,超出了上述公式所表达的范围 (确实如此)。在这种特定的规范语言中,公式\valid(s1+(0 .. n-1))
等同于true
,当n
为0
时,并且不能捕捉到GCC 4.9依赖于优化的未定义行为。
&a + 1
和&b + 1
在不被解引用的情况下是有效的(C11 sec 6.5.6)。但是,如果将memcpy(0, 0, 0)
视为UB,则这也将是UB。 - Drew McGowenint i = 42; int *p = &i + 1; int *q = p;
。在q
的初始化器中引用p
是有效且无害的,只要p
和q
都没有被解引用即可。 - Keith Thompsonmemcpy
函数时长度为零,那么它可以对指针进行的任何操作都是“授权”的吗?即使长度为零,传递null作为src
或dest
也是被禁止的,因为合法的memcpy
允许使用自上而下的复制操作,这又需要计算src+length
和dest+length
。如果src
或dest
为空,则此类计算将是未定义行为,但如果src
和dest
是“one-past”指针且length
为零,则是合法的。 - supercat