【发布时间】:2014-10-12 23:25:51
【问题描述】:
这个问题跟在previous question 之后,是关于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);
这个问题完全值得问,因为标准中有两个“有效”指针的概念:有些指针可以通过指针算法有效地获得,并且可以有效地与<、@987654330 进行比较@ 指向同一对象内的其他指针。并且有些指针对取消引用有效。前一个类在后面的 sn-p 中包含诸如 &a + 1 和 &b + 1 之类的“过去”指针,而后一个类不包含这些为有效的指针。
char a;
const char b = '7';
memcpy(&a + 1, &b + 1, 0);
是否应该将上述 sn-p 视为已定义的行为,因为memcpy() 的参数无论如何都被键入为指向void 的指针,因此它们各自的有效性问题不能与取消引用它们有关。还是应该将&a + 1 和&b + 1 视为“在程序的地址空间之外”?
这对我很重要,因为我正在将标准 C 函数的效果形式化。我已经将memcpy() 的一个前提条件写为requires \valid(s1+(0 .. n-1));,直到有人指出我注意到GCC 4.9 已经开始aggressively 优化此类库函数调用,超出了上面公式中表达的内容(indeed) .当n 为0 时,此特定规范语言中的公式\valid(s1+(0 .. n-1)) 等价于true,并且不捕获GCC 4.9 依赖于优化的未定义行为。
【问题讨论】:
-
您的示例(我相信有人会使用适当的“之前询问”链接对此进行标记)调用未定义的行为,因为您正在访问未由您的程序分配或定义的内存。
-
我相信它不是 UB,因为表达式
&a + 1和&b + 1是 有效(C11 sec 6.5.6)只要因为它们没有被取消引用。但是,如果memcpy(0, 0, 0)被认为是 UB,那么这也是 UB。 -
@JohnH:你可能是对的(我倾向于像你一样相信),但你能引用 C11 标准来捍卫你的主张吗? Pascal Cuoq 非常了解 C ....
-
@JohnH 好吧,
memcpy的规范说它复制了n字符,在我的示例中n是0,所以我不确定“访问”是正确的词在这里。 -
@Leushenko:至少在某些不引用基础对象的上下文中,“一个结束”指针是完全有效的。示例:
int i = 42; int *p = &i + 1; int *q = p;。在q的初始化程序中对p的引用是有效且无害的,只要p和q都没有被取消引用。
标签: c language-lawyer c11 gcc4.9