【问题标题】:How to memset() memory to a certain pattern instead of a single byte?如何将 memset() 内存设置为特定模式而不是单个字节?
【发布时间】:2011-03-21 16:15:22
【问题描述】:

我需要将重复模式写入内存(例如0x11223344),以便整个内存看起来像(十六进制):

1122334411223344112233441122334411223344112233441122334411223344...

我不知道如何使用 memset() 来做这件事,因为它只需要一个字节,而不是 4 个字节。

有什么想法吗?

【问题讨论】:

    标签: c memory design-patterns memset


    【解决方案1】:

    您可以在某处设置序列,然后使用memcpy() 将其复制到您需要的位置。

    【讨论】:

    • memset 只能存储单字节模式,不能存储这样的 4 字节整数
    【解决方案2】:

    递归复制内存,使用你已经填充的区域作为模板,每次迭代 O(log(N)):

    int fillLen = ...;
    int blockSize = 4; // Size of your pattern
    
    memmove(dest, srcPattern, blockSize);
    char * start = dest;
    char * current = dest + blockSize;
    char * end = start + fillLen;
    while(current + blockSize < end) {
        memmove(current, start, blockSize);
        current += blockSize;
        blockSize *= 2;
    }
    // fill the rest
    memmove(current, start, (int)end-current);
    

    我对 O(log(N)) 的意思是运行时将比手动填充内存要快得多,因为 memmove() 通常使用特殊的、手动优化的汇编程序循环,速度非常快。

    【讨论】:

    • memmove 的调用次数为 O(log(n));实际复杂度仍然是 O(n)。
    【解决方案3】:

    一种有效的方法是将指针转换为所需大小(以字节为单位)的指针(例如,uint32_t 为 4 个字节)并填充整数。虽然有点丑。

    char buf[256] = { 0, };
    uint32_t * p = (uint32_t *) buf, i;
    
    for (i = 0; i < sizeof(buf) / sizeof(* p); i++) {
        p[i] = 0x11223344;
    }
    

    未测试!

    【讨论】:

    • 需要注意的一件事是buf 可能无法满足您平台上uint32_t 的对齐要求。如果bufmalloc 的结果,则无需担心这一点,但如果它(例如)通过您无法控制的代码作为参数传入,则需要检查对齐方式在以这种方式写入之前,否则这将导致某些平台上的访问无效。
    • 另一件需要注意的事情可能是字节序,如果这是在一台小字节序计算机上运行并且填充和读取是使用不同大小的类型完成的(即填充 int 但读取 char)
    • 这不是很有效;在我的示例中使用 memmove() 会快得多,因为它使用特殊的汇编器操作和手动优化的代码。
    • @Aaron Digulla:这种说法取决于很多事情:例如,对于小缓冲区,您将被函数调用开销所扼杀,重复对memmove( ) 进行小调用。对于“典型”缓冲区,您的解决方案在大多数具有良好优化库的平台上可能会更快,但对于真正巨大的缓冲区,您的解决方案将渐近地处理两倍的页面错误,并且在大多数平台上几乎慢 2 倍。跨度>
    • 据我所知,代码在一般情况下包含 UB(不仅仅是这个 sn-p - 除了之前提到的对齐方式) - buf 可以作为其他东西访问,然后 charuint32_t 违反严格的指针别名规则 - 所以如果 OP 想要填充 floats (s) 的数组,他会遇到 UB。
    【解决方案4】:

    标准 C 库没有这样的功能。但是 memset 通常被实现为展开循环以最小化分支和条件检查:

    static INLINE void memset4(uint32_t *RESTRICT p, uint32_t val, int len) {
      uint32_t *end = p + (len&~0x1f); //round down to nearest multiple of 32
      while (p != end) { //copy 32 times
        p[ 0] = val;
        p[ 1] = val;
        p[ 2] = val;
        p[ 3] = val;
        p[ 4] = val;
        p[ 5] = val;
        p[ 6] = val;
        p[ 7] = val;
        p[ 8] = val;
        p[ 9] = val;
        p[10] = val;
        p[11] = val;
        p[12] = val;
        p[13] = val;
        p[14] = val;
        p[15] = val;
        p[16] = val;
        p[17] = val;
        p[18] = val;
        p[19] = val;
        p[20] = val;
        p[21] = val;
        p[22] = val;
        p[23] = val;
        p[24] = val;
        p[25] = val;
        p[26] = val;
        p[27] = val;
        p[28] = val;
        p[29] = val;
        p[30] = val;
        p[31] = val;
        p += 32;
      }
      end += len&0x1f; //remained
      while (p != end) *p++ = val; //copy remaining bytes
    }
    

    好的编译器可能会使用一些特定于 CPU 的指令来进一步优化它(例如使用 SSE 128 位存储),但即使没有优化,它也应该与库 memset 一样快,因为这种简单的循环是内存访问受限的.

    【讨论】:

      【解决方案5】:

      如果您的模式适合wchar_t,您可以像使用memset() 一样使用wmemset()

      【讨论】:

        【解决方案6】:

        嗯,正常的做法是手动设置前四个字节,然后memcpy(ptr+4, ptr, len -4)

        这会将前四个字节复制到后四个字节,然后将后四个字节复制到第三个字节,依此类推。

        请注意,这“通常”有效,但不能保证有效,具体取决于您的 CPU 架构和 C 运行时库。

        【讨论】:

        • 如果源缓冲区和目标缓冲区重叠,memcpy 的行为未定义。这可能适用于某些平台,但肯定不适用于许多其他平台。
        • 这将用后四个字节中的任何内容覆盖前四个字节。此外,memcpy 不应与重叠范围一起使用。
        • 您是在评论我的原始消息还是编辑后的消息。 memcpy 是 (dest, src, len),我现在已经正确了。 (我最初把它倒过来了,但我虽然在你发表评论之前已经修好了)
        • 我在评论原件——看到你现在修正了订单。 (我会删除反对票,但您需要编辑才能解锁)
        • 如果你只是为 4 个字节执行此操作,那么直接 *iPtr++ = dwordValue 比调用 memcpy 更好。 memcpy 只有在超过 N 个字节时才会更快。 N 大于 4。不记得该值,但几年前对其进行了一些测试。
        【解决方案7】:

        在 OS X 上,为此使用 memset_pattern4( );我希望其他平台也有类似的 API。

        我不知道一个简单的便携式解决方案,除了用循环填充缓冲区(这非常简单)。

        【讨论】:

        • 我忘了我可以使用循环。谢谢提醒。现在用循环试试。
        猜你喜欢
        • 1970-01-01
        • 2013-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-14
        • 1970-01-01
        相关资源
        最近更新 更多