memset()是否接受大于char的整数?

是否有一个memset()版本设置一个大于1字节(char)的值? 例如,假设我们有一个memset32()函数,所以使用它我们可以执行以下操作:

int32_t array[10]; memset32(array, 0xDEADBEEF, sizeof(array)); 

这将在数​​组的所有元素中设置值0xDEADBEEF。 目前在我看来这只能通过循环来完成。

具体来说,我对64位版本的memset()感兴趣。 知道这样的事吗?

 void memset64( void * dest, uint64_t value, uintptr_t size ) { uintptr_t i; for( i = 0; i < (size & (~7)); i+=8 ) { memcpy( ((char*)dest) + i, &value, 8 ); } for( ; i < size; i++ ) { ((char*)dest)[i] = ((char*)&value)[i&7]; } } 

(解释,按照注释中的要求:当你指定一个指针时,编译器假定指针与类型的自然对齐对齐;对于uint64_t,这是8个字节.memcpy()没有这样的假设。在某些硬件上未对齐访问是不可能的,因此赋值不是一个合适的解决方案,除非您知道未对齐的访问在硬件上工作的损失很小或没有,或者知道它们永远不会发生,或者两者兼而有。编译器将替换小的memcpy()和memset() s有更合适的代码,所以看起来并不那么糟糕;但是如果你确实知道分配将始终有效并且你的分析器告诉你它更快,你可以用一个赋值替换memcpy。第二个用于()如果要填充的内存量不是64位的倍数,则存在循环。如果你知道它总是存在,你可以简单地放弃该循环。)

afaik没有标准的库函数。 因此,如果您正在编写可移植代码,那么您正在查看循环。

如果您正在编写非可移植代码,请检查您的编译器/平台文档,但不要屏住呼吸,因为在这里很少得到很多帮助。 也许其他人会参与提供某些function的平台示例。

您自己编写的方式取决于您是否可以在API中定义调用者保证dst指针与您平台上的64位写入(或者如果是可移植的平台)充分对齐。 在任何具有64位整数类型的平台上,malloc至少会返回适当对齐的指针。

如果你必须应对不对齐,那么你需要像moonshadow的答案。 编译器可以内联/展开大小为8的memcpy(并且如果它们存在则使用32位或64位未对齐的写操作),因此代码应该非常糟糕,但我的猜测是它可能不会特殊情况对齐目的地的整个function。 我很想得到纠正,但我不会害怕。

因此,如果您知道调用者将始终为您的体系结构提供足够对齐的dst,并且长度为8个字节的倍数,那么请执行一个简单的循环来编写uint64_t(或者您的64位int是什么编译器)你可能(没有承诺)最终得到更快的代码。 你肯定会有更短的代码。

无论如何,如果您关心性能,那么请对其进行分析。 如果它不够快,请再次尝试更多优化。 如果它仍然不够快,请问一个关于CPU的asm版本的问题,它的速度不够快。 memcpy / memset可以从每个平台的优化中获得巨大的性能提升。

检查您的操作系统文档以获取本地版本,然后考虑使用该循环。

编译器可能知道更多关于优化任何特定体系结构上的内存访问的知识,所以让它完成工作。

将其作为库包装并使用编译器允许的所有速度提升优化进行编译。

仅供记录,以下使用以下模式中的memcpy(..) 。 假设我们想要用20个整数填充数组:

 -------------------- First copy one: N------------------- Then copy it to the neighbour: NN------------------ Then copy them to make four: NNNN---------------- And so on: NNNNNNNN------------ NNNNNNNNNNNNNNNN---- Then copy enough to fill the array: NNNNNNNNNNNNNNNNNNNN 

这需要memcpy(..) O(lg(num))个应用程序。

 int *memset_int(int *ptr, int value, size_t num) { if (num < 1) return ptr; memcpy(ptr, &value, sizeof(int)); size_t start = 1, step = 1; for ( ; start + step <= num; start += step, step *= 2) memcpy(ptr + start, ptr, sizeof(int) * step); if (start < num) memcpy(ptr + start, ptr, sizeof(int) * (num - start)); return ptr; } 

我认为如果使用一些硬件块内存复制function优化memcpy(..) ,它可能比循环更快,但事实certificate,简单的循环比上面的-O2和-O3更快。 (至少在Windows上使用我的特定硬件使用MinGW GCC。)如果没有-O开关,在400 MBarrays上,上面的代码大约是等效循环的两倍,在我的机器上需要417 ms,而通过优化它们两者都持续约300毫秒。 这意味着它需要与字节大约相同的纳秒数,并且时钟周期约为1纳秒。 因此,我的机器上没有硬件块内存复制function,或者memcpy(..)实现没有利用它。

wmemset(3)是memset的宽(16位)版本。 我认为这是你最接近C的,没有循环。

如果您只是针对x86编译器,您可以尝试类似(VC ++示例):

 inline void memset32(void *buf, uint32_t n, int32_t c) { __asm { mov ecx, n mov eax, c mov edi, buf rep stosd } } 

否则只需做一个简单的循环并相信优化器知道它在做什么,就像这样:

 for(uint32_t i = 0;i < n;i++) { ((int_32 *)buf)[i] = c; } 

如果你让它变得复杂,那么它最终会比简化优化代码更慢,更不用说难以维护了。

您应该让编译器像其他人建议的那样为您优化。 在大多数情况下,循环可以忽略不计。

但是如果这种特殊情况并且您不介意特定于平台,并且确实需要摆脱循环,则可以在汇编块中执行此操作。

 //pseudo code asm { rep stosq ... } 

您可以使用google stosq assembly命令来获取详细信息。 它不应该超过几行代码。

写自己的; 即使在asm中它也是微不足道的。