是否在C11中定义了memcpy(&a + 1,&b + 1,0)?

Pas*_*uoq 32 c language-lawyer c11 gcc4.9

这个问题遵循前一个关于定义的问题memcpy(0, 0, 0),该问题最终被确定为未定义的行为.

正如相关问题所示,答案取决于C11第7.1.4:1条的内容

除非在以下详细说明中另有明确说明,否则以下每个语句均适用:如果函数的参数具有无效值(例如函数域外的值,或程序地址空间外的指针,或空指针,[...])行为未定义.[...]

标准函数memcpy()需要指向voidconst void,如下所示:

void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
Run Code Online (Sandbox Code Playgroud)

这个问题值得一问的,只是因为有很多标准的"有效"指针两个概念都:有可以有效地通过指针算术来获得,并且可以有效地相比指针<,>同一对象内部其他指针.并且有一些指针可用于解除引用.前一类包括"一个过去"指针,例如&a + 1&b + 1下面的代码片段,而后一个类不包括这些指针有效.

char a;
const char b = '7';
memcpy(&a + 1, &b + 1, 0);
Run Code Online (Sandbox Code Playgroud)

如果将上述片段视为已定义的行为,memcpy()则根据void无论如何将参数键入为指针的事实,因此其各自有效性的问题不能解除对它们的解除引用.还是应该&a + 1&b + 1被认为是"程序的地址空间之外"?

这对我很重要,因为我正在使标准C函数的效果正式化.我写的一个先决条件memcpy()requires \valid(s1+(0 .. n-1));,直到有人指出我的注意,GCC 4.9已经开始积极优化这样的库函数调用超出了公式中表示上述(确实).式\valid(s1+(0 .. n-1))在本具体说明书的语言是相当于truen0,并且不捕获该未定义行为GCC 4.9依赖于优化.

oua*_*uah 18

C11说:

(C11,7.24.2.1p2)"memcpy函数将s2指向的对象中的n个字符复制到s1指向的对象中."

&a + 1本身是一个指向整数加法的有效指针,但&a + 1它不是指向对象的指针,因此调用会调用未定义的行为.

  • 另请参见C11 7.24.1p2:"...**`n`**在调用该函数时可以为零.除非在本子条款中对特定函数的描述中另有明确说明,否则指针参数如此如7.1.4所述,呼叫**仍应具有有效值**.(重点补充).如果您愿意,请随意将其添加到您的答案中. (6认同)
  • 我不相信.这里的指针不是无效值,因此导致`memcpy(0,0,0)`的推理不适用.引用文本并未说s2必须指向一个对象; "由s2指向"是一个谓词,用于描述从中取出字符的位置,但如果不采用任何字符,则不会读取不包含值的内存. (2认同)
  • @MattMcNabb我之前想要提交一份关于无关C标准问题的DR(惊讶,惊讶),并且如果不成为某种国家组织的付费会员,你就不能做到这一点.我想我会继续提交我的DR作为SO问题,并希望有权利的人能够接受他们. (2认同)

小智 5

虽然根据标准的"正确"答案似乎不同意,但我发现它只是不诚实的,在int [6]之后; int b [6]; 所有的

memcpy(a+0, b+0, 6);
memcpy(a+1, b+1, 5);
memcpy(a+2, b+2, 4);
memcpy(a+3, b+3, 3);
memcpy(a+4, b+4, 2);
memcpy(a+5, b+5, 1);
Run Code Online (Sandbox Code Playgroud)

应该是有效的(并复制一个以数组末尾结尾的区域)

memcpy(a+6, b+6, 0);
Run Code Online (Sandbox Code Playgroud)

根据计数而不是地址有效.这是复制区域的同一端!

就个人而言,我倾向于定义memcpy(0,0,0)也是有效的(只需要有效指针但没有对象的理由)但至少它是一个单一的情况,而"数组结束"的情况是一个用于复制数组末尾区域的常规模式的实际异常.