Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 写入符合严格别名的memcpy_C++_C_Memcpy_Strict Aliasing - Fatal编程技术网

C++ 写入符合严格别名的memcpy

C++ 写入符合严格别名的memcpy,c++,c,memcpy,strict-aliasing,C++,C,Memcpy,Strict Aliasing,当问“如何实现符合严格别名规则的memcpy函数”时,一般的答案大致如下: void *memcpy(void *dest, const void *src, size_t n) { for (size_t i = 0; i < n; i++) ((char*)dest)[i] = ((const char*)src)[i]; return dest; } void*memcpy(void*dest,const void*src,size\u t n) {

当问“如何实现符合严格别名规则的memcpy函数”时,一般的答案大致如下:

void *memcpy(void *dest, const void *src, size_t n)
{
    for (size_t i = 0; i < n; i++)
        ((char*)dest)[i] = ((const char*)src)[i];
    return dest;
}
void*memcpy(void*dest,const void*src,size\u t n)
{
对于(大小i=0;i
然而,如果我理解正确,编译器可以自由地对memcpy调用和dest的访问进行重新排序,因为它可以对char*的写入和任何其他指针类型的读取进行重新排序(严格的别名规则仅防止对char*的读取和任何其他指针类型的写入进行重新排序)

这是正确的吗?如果是的话,有没有正确实现memcpy的方法,或者我们应该仅仅依靠内置的memcpy


请注意,这个问题不仅涉及memcpy,还涉及任何反序列化/解码函数。

IANALL,但我认为编译器不允许以您描述的方式把事情搞砸。在规范中,严格别名是通过非法指针类型呈现对对象的未定义访问,而不是通过在对象访问上指定另一个复杂的偏序来“实现”的。

是的,您缺少一些东西。编译器可能需要重新排序写入
dest
和读取
dest
。现在,由于从
src
读取发生在写入
dest
之前,而您的假设从
dest
读取发生在写入
dest
之后,因此从
dest
读取发生在从
src
读取之后

严格的别名规则专门排除对
char
类型的强制转换(请参见下面最后一点),因此编译器将在您的情况下执行正确的操作。只有在将
int
转换为
short
时,类型双关才是一个问题。在这里,编译器可能会做出会导致未定义行为的假设

C99§6.5/7:

对象的存储值只能由具有以下类型之一的左值表达式访问:

  • 与对象的有效类型兼容的类型
  • 与对象的有效类型兼容的类型的合格版本
  • 与对象的有效类型相对应的有符号或无符号类型
  • 与对象有效类型的限定版本相对应的有符号或无符号类型
  • 在其成员中包含上述类型之一的聚合或联合类型(递归地包括子聚合或包含的联合的成员),或
  • 字符类型
由于
(char*)dest
(char const*)src
都指向
char
,编译器必须假定它们可能是别名。此外,还有一条规则规定,指向字符类型的指针可以别名任何内容

所有这些都与
memcpy
无关,因为实际签名是:

void* memcpy( void* restrict dest, void* restrict src, size_t n );
它告诉编译器不能有别名,因为用户保证它。不能使用
memcpy
复制重叠区域,而不会导致未定义的行为


无论如何,给定的实现没有问题。

这里每个人似乎都缺少的是严格别名(6.5/7)取决于术语有效类型(6.5/6)。有效类型对函数
memcpy
(6.5/6)有明确的特殊规则:

如果使用将值复制到没有声明类型的对象中
memcpy
memmove
,或作为字符类型数组复制,则该访问和未修改值的后续访问的修改对象的有效类型为复制值的对象的有效类型(如果有)

因此,我认为在
memcpy
函数中使用严格的别名是没有意义的。只有知道有效类型时,才能谈论严格别名。那么,基于上述情况,您如何确定这一点?
memcpy
的内部是否是带有
memcpy
的副本

这就像说“为了理解memcpy中使用的有效类型,您必须首先理解memcpy中使用的有效类型”


因此,我不太明白这个问题或发布的任何答案有什么意义。

如果一个对象没有声明的类型,那么它可能获得的任何有效类型只有在下次修改该对象之前才有效。使用字符类型的指针写入对象视为修改对象,从而取消设置旧类型,但通过字符类型指针写入对象不会设置新类型,除非此类操作作为“复制为字符类型数组”的一部分发生,不管这意味着什么。没有有效类型的对象可以合法读取任何类型的对象

由于“复制为字符类型数组”的有效类型语义与
memcpy
的相同,因此可以使用字符指针来编写memcpy实现。它可能不会按照
memcpy
允许的方式设置目的地的有效类型,但如果目的地没有有效类型,则在使用
memcpy
时定义的任何行为都将以相同的方式定义[正如IMHO在
memcpy
中本应如此]

我不确定是谁想出了这样一个想法:编译器可以假设获得了有效类型的存储在使用
char*
修改时会保留该有效类型,但标准中没有任何内容可以证明这一点。如果您需要您的代码与gcc一起工作,请指定它必须与
-fno strict aliasing
标志一起使用,除非或直到gcc开始遵守该标准。没有任何理由试图支持一个编译器,而该编译器的作者