memcpy别名int到char?

memcpy别名int到char?,c,undefined-behavior,memcpy,strict-aliasing,C,Undefined Behavior,Memcpy,Strict Aliasing,严格的别名让我多疑。有时,我使用*int指针设置值,并期望目标内存读取相同的数据,而不管读取指针的类型是什么。严格的别名不能保证这一点,有时甚至会导致情况并非如此 如果我在一个循环中读取一个char[],并且在这个char[]数组中有一个*int链接,那么我就破坏了其他标准C语言中的别名规则 我正在制作一个JIT编译器,因为我使用的是x86,所以我确信我不必关心int对齐。让我们把它排除在方程之外,直到我们解决了别名问题 考虑一下这个片段: unsigned char x86[] = {0x11

严格的别名让我多疑。有时,我使用*int指针设置值,并期望目标内存读取相同的数据,而不管读取指针的类型是什么。严格的别名不能保证这一点,有时甚至会导致情况并非如此

如果我在一个循环中读取一个char[],并且在这个char[]数组中有一个*int链接,那么我就破坏了其他标准C语言中的别名规则

我正在制作一个JIT编译器,因为我使用的是x86,所以我确信我不必关心int对齐。让我们把它排除在方程之外,直到我们解决了别名问题

考虑一下这个片段:

unsigned char x86[] = {0x11, 0x44, 0x42, ... };
uint32_t *specific_imm = (x86+10);
现在,*specific_imm=42;在x86平台上仍然是UB,因为编译器可以假设*specific_imm没有与x86[]混淆。通过这样的假设,它不需要立即设置这些字节,但可以进行各种优化。将x86[]和*specific_imm都设置为volatile可以解决我的问题,但这还不够好,因为我想正确地学习C

我们现在已经解决了别名问题。有些人建议采用这种解决办法: memcpy(x86+10,具体imm,4)

但是C标准在别名指针方面似乎也有问题(如果我理解正确的话),如下代码所示

/* naive implementation of memcpy */
inline void _memcpy(unsigned char *a, unsigned char *b){
  *a = *b;
}

int main(void) {
  long i = 0xFFFFFFFF;
  unsigned char c = 1;
  ++i;
  _memcpy(&c,&i);
  return c;
}
由于编译器可以自由地假设“i”在这种情况下不会影响c(?),main可以自由地进行优化,只返回1

在直接着手解决问题之前,我对解决问题更感兴趣


提前谢谢你,你错了。C编译器不能假定任意指针和指向字符变体的指针没有别名。它也不能假定指向有符号和无符号int的两个指针,或指向有符号和无符号long等的两个指针未对齐


在上一个示例中,任何理智的软件开发人员都会将其编译器警告设置为无法编译

你错了。C编译器不能假定任意指针和指向字符变体的指针没有别名。它也不能假定指向有符号和无符号int的两个指针,或指向有符号和无符号long等的两个指针未对齐

在上一个示例中,任何理智的软件开发人员都会将其编译器警告设置为无法编译

通过这样的假设,它不需要立即设置这些字节,但可以进行各种优化

根本不需要设置它们。它可以做任何事


将x86[]和*specific_imm都设置为volatile可以解决我的问题

不是真的。严格别名表示某个变量不能通过指向不相关类型的指针进行更改。这样做会导致程序执行标准未指定的操作。通常这表现在各种与优化器相关的错误中,但不一定如此。该程序也可能什么都不做,或者崩溃并烧掉

volatile
无法解决此问题(特别是因为您将指针声明为指向
volatile
数据的对象,而不是使实际数据变量
volatile

一些编译器(如GCC)在优化代码时假设您的程序永远不会违反严格的别名(从而调用未定义的行为)。但这并不意味着关闭优化将删除未定义的行为本身,它只会关闭优化器依赖性,即假定您的程序没有调用未定义的行为。它不会修复实际的bug


有人提出了这个解决方案:memcpy

这将解决问题,因为有效类型的规则。6.5/6:

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

这符合严格别名规则的第一部分,6.5/7:

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

-与对象的有效类型兼容的类型


但是C标准在别名指针方面似乎也有问题(如果我理解正确的话)

不,那是不对的。由于上述原因,real memcpy函数使用void指针,并且不能违反严格别名。您的自制版本使用了
无符号字符*
,这也很好,6.5/7:

-字符类型

请特别阅读

通过这样的假设,它不需要立即设置这些字节,但可以进行各种优化

根本不需要设置它们。它可以做任何事


将x86[]和*specific_imm都设置为volatile可以解决我的问题

不是真的。严格别名表示某个变量不能通过指向不相关类型的指针进行更改。这样做会导致程序执行标准未指定的操作。通常这表现在各种与优化器相关的错误中,但不一定如此。该程序也可能什么都不做,或者崩溃并烧掉

volatile
无法解决此问题(特别是因为您将指针声明为指向
volatile
数据的对象,而不是使实际数据变量
volatile

一些编译器(如GCC)在优化代码时假设您的程序永远不会违反严格的别名(从而调用未定义的行为)。但这并不意味着关闭优化将删除未定义的行为本身,它只会关闭优化器依赖