在学习c的同时,我实现了自己的memcpy功能。我在函数中使用了更宽泛的类型(uint32_t
)。 (为简单起见,函数仅限于4的倍数且数据正确对齐的类型)
void memcpy4( void* dst , void* src , int size )
{
size /= 4;
for ( int i = 0 ; i < size ; i++ )
((uint32_t*)dst)[i] = ((uint32_t*)src)[i];
}
我对类型惩罚和严格别名做了一些阅读,我相信上面的功能打破了规则。正确的实现是这样的,因为你可以使用char:
void memcpy4( void* dst , void* src , int size )
{
for ( int i = 0 ; i < size ; i++ )
((char *)dst)[i] = ((char *)src)[i];
}
我试图通过联盟进行一些投射,但结果证明这也是无效的。
如何使用更宽的类型实现此类函数,而不是破坏严格的别名规则?
答案 0 :(得分:17)
使用多个单字节副本实现memcpy
的方法是使用非标准C。
标准C不支持使用非字符类型实现memcpy
。
Quality C实现提供了一个优化的memcpy
实现,它使用多个单字节副本执行高效复制,但它们使用特定于实现的代码来执行此操作。他们可以通过使用诸如memcpy
之类的开关编译-fnostrict-aliasing
实现来告诉编译器在代码中违反别名规则,依靠特定C实现的已知功能来确保代码将起作用(如果你编写编译器,你可以设计它以便memcpy
的实现工作),或者用汇编语言编写memcpy
。
此外,C实现可以优化它们出现在源代码中的memcpy
调用,用直接指令替换它们来执行操作,或者简单地改变程序的内部语义。 (例如,如果将a
复制到b
,编译器可能根本不会执行复制,但可能只是从后续代码访问a
的{{1}}加载。)
要在违反别名规则的情况下实现您自己的专用复制操作,请使用b
进行编译,如果您使用的是GCC或Clang。如果您使用的是其他编译器,请检查其文档以获取禁用别名规则的选项。 (注意:Apple使用的GCC默认禁用严格别名,接受-fnostrict-aliasing
但不接受-fstrict-aliasing
。我假设非Apple GCC接受-fnostrict-aliasing
。)
如果您使用的是良好的C实现,您可能会发现-fnostrict-aliasing
的四字节副本实现的效果不如原始memcpy4
,具体取决于具体情况。