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

Mat*_* M. 72 c memcpy null-pointer language-lawyer

我不太熟悉C标准,所以请耐心等待.

我想知道,按标准保证memcpy(0,0,0)是否安全.

我能找到的唯一限制是,如果内存区域重叠,那么行为是未定义的......

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

tem*_*def 67

我有一个C标准的草案版本(ISO/IEC 9899:1999),它有一些有趣的事情可以说.首先,它在关于提到(§7.21.1/ 2)memcpy

如果声明为size_tn 的参数指定了函数数组的长度,则在调用该函数时,n的值可以为零.除非在本子条款中对特定函数的描述中另有明确说明,否则此类调用上的指针参数仍应具有有效值,如7.1.4中所述.在这样的调用中,定位字符的函数不会发生,比较两个字符序列的函数返回零,复制字符的函数复制零个字符.

此处指出的参考指向:

如果函数的参数具有无效值(例如函数域外的值,或程序地址空间外的 指针,或空指针,或指向不可修改存储的指针,则相应参数不具有const限定条件)或具有可变数量参数的函数不期望的类型(提升后),行为未定义.

所以看起来根据C规范,调用

memcpy(0, 0, 0)
Run Code Online (Sandbox Code Playgroud)

导致未定义的行为,因为空指针被视为"无效值".

也就是说,memcpy如果您执行此操作,如果任何实际执行中断,我会感到非常惊讶,因为如果您说要复制零字节,我能想到的大多数直观实现都不会做任何事情.

  • @MattMcNabb该实现被破坏了. (11认同)
  • 你将在生产中使用的任何实现都不会产生除了这样的调用之外的任何操作,但是允许并且是合理的实现...例如,具有错误检查的C解释器或扩充编译器拒绝打电话,因为它不符合要求.当然,如果标准允许调用那么这是不合理的,就像`realloc(0,0)`那样.用例类似,我已经使用过它们(请参阅我在问题下的评论).标准制造这个UB是毫无意义和不幸的. (8认同)
  • "如果你这样做,任何实际的memcpy实现都会让我感到非常惊讶" - 我已经用了一个会这样做的; 事实上,如果你使用有效指针传递长度0,它实际上复制了65536个字节.(它的循环减少了长度然后测试). (4认同)
  • 我可以肯定标准草案中引用的部分在最终文件中是相同的.这样的呼叫不应该有任何麻烦,但它依然是你所依赖的未定义的行为.所以"保证"的答案是"不". (3认同)
  • @MattMcNabb:也许是"正确"添加到"实际".我想我们都对旧的贫民窟C图书馆有着如此美好的回忆,而且我不确定我们中有多少人会欣赏那些被召回的记忆.:) (3认同)
  • 数组的末尾是否算作有效值? (2认同)

use*_*586 21

只是为了好玩,gcc-4.9的发行说明表明它的优化器使用了这些规则,例如可以删除条件

int copy (int* dest, int* src, size_t nbytes) {
    memmove (dest, src, nbytes);
    if (src != NULL)
        return *src;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

然后在copy(0,0,0)调用时给出意想不到的结果(参见https://gcc.gnu.org/gcc-4.9/porting_to.html).

我对gcc-4.9的行为有些矛盾; 行为可能符合标准,但能够调用memmove(0,0,0)有时是对这些标准的有用扩展.

  • 有人请告诉我,为什么我没有获得stackoverflow徽章"开始了一场火焰战":-) (8认同)
  • 有趣.我理解你的矛盾心理,但这是C中优化的核心:编译器*假定*开发人员遵循某些规则,因此*推断*某些优化是有效的(如果遵循规则,它们就是这样). (2认同)
  • @tmyklebu:给定`char *p = 0; int i=something;`,即使 `i` 为零,对表达式 `(p+i)` 的评估也会产生未定义的行为。 (2认同)