在标准C中从头开始实现memcpy在技术上是不可能的吗?

Jas*_*rff 13 c char memcpy language-lawyer

霍华德朱写道:

在最新的C规范中,不可能编写malloc或memcpy的"合法"实现.

这是正确的吗?我的印象是,在过去,标准的意图(至少)是这样的事情会起作用:

void * memcpy(void * restrict destination, const void * restrict source, size_t nbytes)
{
    size_t i;
    unsigned char *dst = (unsigned char *) destination;
    const unsigned char *src = (const unsigned char *) source;

    for (i = 0; i < nbytes; i++)
        dst[i] = src[i];
    return destination;
}
Run Code Online (Sandbox Code Playgroud)

这里违反了最新C标准中的哪些规则?或者,memcpy此代码未正确实现规范的哪些部分?

Lun*_*din 1

TL;DR
应该没问题,只要是memcpy基于幼稚的逐字符复制即可。

并且没有针对移动可在单个指令中复制的最大对齐类型大小的块进行优化。后者是标准库实现的方式。


令人担忧的是这样的场景:

void* my_int = malloc(sizeof *my_int);
int another_int = 1;

my_memcpy(my_int, &another_int, sizeof(int));

printf("%d", *(int*)my_int); // well-defined or strict aliasing violation?
Run Code Online (Sandbox Code Playgroud)

解释:

  • 我指向的数据my_int没有有效类型。
  • 当我们将数据复制到该my_int位置时,人们可能会担心我们强制有效类型变为unsigned char,因为这就是my_memcpy使用的类型。
  • 然后当我们通过 读取该内存位置时int*。我们会违反严格的别名吗?

然而,这里的关键是有效类型规则中的一个特殊例外,在 C17 6.5/6 中指定,重点是我的:

如果使用 memcpy或将值复制到没有声明类型的对象中memmove或者将其复制为字符类型数组则该访问以及不修改该值的后续访问的已修改对象的有效类型就是有效类型。从中复制值的对象(如果有)。

由于我们确实将数组复制为字符类型,因此指向的有效类型将成为从中复制值的my_int对象的有效类型。another_int

所以一切都应该没问题。

此外,您还restrict对参数进行了限定,因此对于两个指针是否可能互相别名(就像 real 一样),应该不会有什么大惊小怪memcpy

值得注意的是,这条规则在 C99、C11 和 C17 中一直保持不变。有人可能会说这是编译器供应商滥用的非常糟糕的规则,但那是另一回事了。