【问题标题】:Memcpy implementation, strict aliasingMemcpy 实现,严格别名
【发布时间】:2014-02-08 15:02:30
【问题描述】:

在学习 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];
}

我对类型双关语和严格别名做了一些阅读,我相信上面的函数违反了规则。正确的实现是这样的,因为您可以使用字符:

void memcpy4( void* dst , void* src , int size )
{
    for ( int i = 0 ; i < size ; i++ )
        ((char *)dst)[i] = ((char *)src)[i];
}

我尝试通过联合进行一些强制转换,但结果也无效。

怎样才能用更宽的类型来实现这样的功能而不违反严格的别名规则?

【问题讨论】:

  • 由于这是一个学习练习,让我建议您学习另一件事:永远不要使用有符号整数作为大小和索引。使用无符号整数,或更好的 std::size_t。这种memcpy() 的实现是基于signed int 攻击的经典示例。
  • 您的实现使用uint32_tuint32_t 有多大?我不知道 - 我知道我可能会猜到它会是什么,但我不知道 - 我绝对不知道在任何和所有平台上。试试size /= sizeof(uint32_t)
  • 一个正确的实现必须处理这样一个事实,即有问题的指针(源和目标)可能相对于对特定架构可能重要的任何边界都未对齐。我知道这只是一个练习,但我鼓励你坐下来处理所有的边缘条件。这就是学习的方式。
  • sizeof(uint32_t)通常为 4,但在 CHAR_BIT &gt; 8 的某些平台上可能小于此值。
  • @self.: uint32_t 被定义为 32 位,而不是 4 字节。字节不要求为 8 位,而且有很多平台不是。

标签: c++ c memcpy strict-aliasing


【解决方案1】:

使用多个单字节副本实现memcpy的方法是使用非标准C。

标准 C 不支持使用非字符类型实现 memcpy

Quality C 实现提供了优化的memcpy 实现,它使用多于单字节的副本来执行高效的复制,但它们使用特定于实现的代码来执行此操作。他们可以通过使用诸如-fnostrict-aliasing 之类的开关来编译memcpy 实现来告诉编译器在代码中将违反别名规则,通过依赖特定C 实现的已知特性来确保代码能够工作(如果你编写编译器,你可以设计它以便你的memcpy 的实现工作),或者用汇编语言编写memcpy

此外,C 实现可以优化memcpy 在源代码中出现的调用,用执行操作的直接指令或简单地更改程序的内部语义来替换它们。 (例如,如果将a 复制到b,编译器可能根本不执行复制,而可能只是从a 加载,随后代码访问b。)

要在违反别名规则的情况下实现您自己的专用复制操作,如果您使用的是 GCC 或 Clang,请使用 -fnostrict-aliasing 编译它。如果您使用的是其他编译器,请查看其文档以获取禁用别名规则的选项。 (注意:我使用的 Apple GCC 默认禁用严格别名并接受 -fstrict-aliasing 但不接受 -fnostrict-aliasing。我假设非 Apple GCC 接受 -fnostrict-aliasing。)

如果您使用的是良好的 C 实现,您可能会发现 memcpy4 的四字节副本实现的性能不如原生 memcpy,具体取决于具体情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 2015-10-15
    • 1970-01-01
    • 1970-01-01
    • 2019-02-09
    • 2018-03-04
    相关资源
    最近更新 更多